diff --git a/.gitattributes b/.gitattributes index d47e847c..540af298 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ *.journal text eol=lf *.feature text eol=lf +poetry.lock text eol=lf +pyrpoject.toml text eol=lf diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml index c5015d5f..396fc854 100644 --- a/.github/workflows/testing.yaml +++ b/.github/workflows/testing.yaml @@ -23,15 +23,15 @@ jobs: if: > ! contains(github.event.head_commit.message, '[ci skip]') runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.python-version == '3.10-dev' }} strategy: + fail-fast: false matrix: - python-version: [ 3.7, 3.8, 3.9 ] + python-version: [ 3.7, 3.8, 3.9, 3.10-dev ] os: [ ubuntu-latest, macos-latest, windows-latest ] - exclude: # Added for GitHub Actions PR problem 2020-12-19 -- remove later! - - os: windows-latest - python-version: 3.9 steps: + - run: git config --global core.autocrlf false - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -46,27 +46,50 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('poetry.lock') }}-${{ matrix.python-version }}-v2 - name: Install dependencies + if: ${{ matrix.python-version != '3.10-dev' }} run: | + echo '::group::poetry' pip install poetry poetry config --local virtualenvs.in-project true + echo '::endgroup::' + + echo '::group::Other dependencies' poetry install --remove-untracked + echo '::endgroup::' + + echo 'DEPS_INSTALLED=true' >> $GITHUB_ENV + + + - name: Install dependencies (Prerelease) + if: ${{ matrix.python-version == '3.10-dev' }} + run: | + echo '::group::poetry' + pip install poetry==1.2.0a1 + poetry config --local virtualenvs.in-project true + echo '::endgroup::' + + echo '::group::Other dependencies' + poetry install --remove-untracked --no-dev --extras testing + echo '::endgroup::' + + echo 'DEPS_INSTALLED=true' >> $GITHUB_ENV - name: Code formatting (Black) - if: success() || failure() + if: ${{ matrix.python-version != '3.10-dev' && env.DEPS_INSTALLED == 'true' }} run: | poetry run black --version poetry run black --check --diff . - - name: Code Style (PyFlakes) - if: success() || failure() + - name: Code Style (flake8) + if: ${{ matrix.python-version != '3.10-dev' && env.DEPS_INSTALLED == 'true' }} run: | - poetry run pyflakes --version - poetry run pyflakes jrnl features tests + poetry run pflake8 --version + poetry run pflake8 jrnl features tests - name: Test with pytest - if: success() || failure() + if: ${{ env.DEPS_INSTALLED == 'true' }} run: poetry run pytest --junitxml=reports/pytest/results.xml - name: Test with behave - if: success() || failure() + if: ${{ env.DEPS_INSTALLED == 'true' }} run: poetry run behave --no-skipped --format progress2 --junit --junit-directory reports/behave diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d28c70..b43053e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,17 +6,29 @@ **Implemented enhancements:** -- Multiple entries with same timestamp should be grouped together [\#1247](https://github.com/jrnl-org/jrnl/issues/1247) +- Python 3.10 support [\#1270](https://github.com/jrnl-org/jrnl/issues/1270) **Build:** +- Move test suite entirely to Pytest \(replace Behave\) [\#1192](https://github.com/jrnl-org/jrnl/issues/1192) +- Remove useless shebangs and executable permissions [\#1283](https://github.com/jrnl-org/jrnl/pull/1283) ([musicinmybrain](https://github.com/musicinmybrain)) +- Add Python 3.10 support [\#1271](https://github.com/jrnl-org/jrnl/pull/1271) ([micahellison](https://github.com/micahellison)) - Remove `--version` from brew release workflow [\#1233](https://github.com/jrnl-org/jrnl/pull/1233) ([wren](https://github.com/wren)) +- Move test suite to Pytest \(replace Behave\) [\#1193](https://github.com/jrnl-org/jrnl/pull/1193) ([wren](https://github.com/wren)) + +**Documentation:** + +- Create security.md [\#1284](https://github.com/jrnl-org/jrnl/issues/1284) +- Document keyring security concerns on Windows, Mac [\#1142](https://github.com/jrnl-org/jrnl/issues/1142) +- Add documentation about saved passwords in Windows [\#1301](https://github.com/jrnl-org/jrnl/pull/1301) ([micahellison](https://github.com/micahellison)) **Packaging:** +- Bump python-dateutil from 2.8.1 to 2.8.2 [\#1302](https://github.com/jrnl-org/jrnl/pull/1302) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump black from 21.5b1 to 21.5b2 [\#1254](https://github.com/jrnl-org/jrnl/pull/1254) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump black from 21.5b0 to 21.5b1 [\#1244](https://github.com/jrnl-org/jrnl/pull/1244) ([dependabot[bot]](https://github.com/apps/dependabot)) - Bump black from 20.8b1 to 21.5b0 [\#1241](https://github.com/jrnl-org/jrnl/pull/1241) ([dependabot[bot]](https://github.com/apps/dependabot)) +- Bump pytest from 6.2.3 to 6.2.4 [\#1240](https://github.com/jrnl-org/jrnl/pull/1240) ([dependabot[bot]](https://github.com/apps/dependabot)) ## [v2.8.1](https://pypi.org/project/jrnl/v2.8.1/) (2021-04-24) diff --git a/Makefile b/Makefile index 1c8a52a6..a4494ee3 100644 --- a/Makefile +++ b/Makefile @@ -18,12 +18,19 @@ format: ## Format files to match style lint: ## Check style with various tools poetry check - poetry run pyflakes . + poetry run pflake8 jrnl tests poetry run black --check --diff . -test: lint ## Run unit tests and behave tests - poetry run pytest - poetry run behave --no-skipped --format progress2 +unit: # unit tests + poetry run pytest tests/unit + +bdd: # bdd tests + poetry run pytest tests/bdd --gherkin-terminal-reporter --tb=native --diff-type=unified + +bdd-debug: # bdd tests + poetry run pytest tests/bdd --gherkin-terminal-reporter --tb=native --diff-type=unified -x -vv + +test: lint unit bdd ## Run unit tests and behave tests build: poetry build diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..c239b957 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security + +If you've discovered a potential security issue in jrnl, please contact the maintainers at [jrnl-sh@googlegroups.com](mailto:jrnl-sh@googlegroups.com). + +You can also feel free to [open an issue](https://github.com/jrnl-org/jrnl/issues/new/choose) (but please don't disclose the vulnerability) in case the email goes to spam. + +You can find [known privacy and security issues in our documentation](https://jrnl.sh/en/stable/privacy-and-security/). diff --git a/docs/privacy-and-security.md b/docs/privacy-and-security.md index 39f4863c..c2996d25 100644 --- a/docs/privacy-and-security.md +++ b/docs/privacy-and-security.md @@ -86,6 +86,18 @@ you have a journal, where that journal file is, and when you last edited it. With a sufficient power imbalance, someone may be able to force you to unencrypt it through non-technical means. +## Saved Passwords + +When creating an encrypted journal, you'll be prompted as to whether or not you +want to "store the password in your keychain." This keychain is accessed using +the [Python keyring library](https://pypi.org/project/keyring/), which has different +behavior depending on your operating system. + +In Windows, the keychain is the Windows Credential Manager (WCM), which can't be locked +and can be accessed by any other application running under your username. If this is +a concern for you, you may not want to store your password. + + ## Notice any other risks? Please let the maintainers know by [filing an issue on GitHub](https://github.com/jrnl-org/jrnl/issues). diff --git a/docs_theme/index.html b/docs_theme/index.html old mode 100755 new mode 100644 diff --git a/features/steps/core.py b/features/steps/core.py index ac5d8950..2ad5bcc4 100644 --- a/features/steps/core.py +++ b/features/steps/core.py @@ -17,7 +17,7 @@ import keyring import toml import yaml -from yaml.loader import FullLoader +from yaml.loader import SafeLoader import jrnl.time @@ -409,7 +409,7 @@ def run(context, command, text=""): if "config_path" in context and context.config_path is not None: with open(context.config_path) as f: - context.jrnl_config = yaml.load(f, Loader=yaml.FullLoader) + context.jrnl_config = yaml.load(f, Loader=yaml.SafeLoader) else: context.jrnl_config = None @@ -418,7 +418,7 @@ def run(context, command, text=""): command = command.format(cache_dir=cache_dir) if "config_path" in context and context.config_path is not None: with open(context.config_path, "r") as f: - cfg = yaml.load(f, Loader=FullLoader) + cfg = yaml.load(f, Loader=SafeLoader) context.jrnl_config = cfg args = split_args(command) @@ -675,7 +675,7 @@ def check_journal_entries(context, number, journal_name="default"): @when("the journal directory is listed") def list_journal_directory(context, journal="default"): with open(context.config_path) as config_file: - configuration = yaml.load(config_file, Loader=yaml.FullLoader) + configuration = yaml.load(config_file, Loader=yaml.SafeLoader) journal_path = configuration["journals"][journal] for root, dirnames, f in os.walk(journal_path): for file in f: diff --git a/jrnl/DayOneJournal.py b/jrnl/DayOneJournal.py index 6e1b8345..01b934e5 100644 --- a/jrnl/DayOneJournal.py +++ b/jrnl/DayOneJournal.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python - -from datetime import datetime +import datetime import fnmatch import os from pathlib import Path @@ -80,35 +78,35 @@ class DayOne(Journal.Journal): entry.creator_device_agent = dict_entry["Creator"][ "Device Agent" ] - except: + except: # noqa: E722 pass try: entry.creator_generation_date = dict_entry["Creator"][ "Generation Date" ] - except: + except: # noqa: E722 entry.creator_generation_date = date try: entry.creator_host_name = dict_entry["Creator"]["Host Name"] - except: + except: # noqa: E722 pass try: entry.creator_os_agent = dict_entry["Creator"]["OS Agent"] - except: + except: # noqa: E722 pass try: entry.creator_software_agent = dict_entry["Creator"][ "Software Agent" ] - except: + except: # noqa: E722 pass try: entry.location = dict_entry["Location"] - except: + except: # noqa: E722 pass try: entry.weather = dict_entry["Weather"] - except: + except: # noqa: E722 pass self.entries.append(entry) self.sort() @@ -118,7 +116,7 @@ class DayOne(Journal.Journal): """Writes only the entries that have been modified into plist files.""" for entry in self.entries: if entry.modified: - utc_time = datetime.utcfromtimestamp( + utc_time = datetime.datetime.utcfromtimestamp( time.mktime(entry.date.timetuple()) ) diff --git a/jrnl/Entry.py b/jrnl/Entry.py old mode 100755 new mode 100644 index 67ba84f3..56347770 --- a/jrnl/Entry.py +++ b/jrnl/Entry.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html -from datetime import datetime +import datetime import re import ansiwrap @@ -15,7 +14,7 @@ from .color import highlight_tags_with_background_color class Entry: def __init__(self, journal, date=None, text="", starred=False): self.journal = journal # Reference to journal mainly to access its config - self.date = date or datetime.now() + self.date = date or datetime.datetime.now() self.text = text self._title = None self._body = None diff --git a/jrnl/FolderJournal.py b/jrnl/FolderJournal.py index e727cdf0..954a9436 100644 --- a/jrnl/FolderJournal.py +++ b/jrnl/FolderJournal.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/Journal.py b/jrnl/Journal.py index 4196571d..6d3c6886 100644 --- a/jrnl/Journal.py +++ b/jrnl/Journal.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html -from datetime import datetime +import datetime import logging import os import re @@ -135,7 +134,9 @@ class Journal: for match in date_blob_re.finditer(journal_txt): date_blob = match.groups()[0] try: - new_date = datetime.strptime(date_blob, self.config["timeformat"]) + new_date = datetime.datetime.strptime( + date_blob, self.config["timeformat"] + ) except ValueError: # Passing in a date that had brackets around it new_date = time.parse(date_blob, bracketed=True) @@ -219,7 +220,10 @@ class Journal: # If strict mode is on, all tags have to be present in entry tagged = self.search_tags.issubset if strict else self.search_tags.intersection - excluded = lambda tags: len([tag for tag in tags if tag in excluded_tags]) > 0 + + def excluded(tags): + return 0 < len([tag for tag in tags if tag in excluded_tags]) + if contains: contains_lower = contains.casefold() @@ -348,7 +352,7 @@ class LegacyJournal(Journal): """Parses a journal that's stored in a string and returns a list of entries""" # Entries start with a line that looks like 'date title' - let's figure out how # long the date will be by constructing one - date_length = len(datetime.today().strftime(self.config["timeformat"])) + date_length = len(datetime.datetime.today().strftime(self.config["timeformat"])) # Initialise our current entry entries = [] @@ -358,7 +362,7 @@ class LegacyJournal(Journal): line = line.rstrip() try: # try to parse line as date => new entry begins - new_date = datetime.strptime( + new_date = datetime.datetime.strptime( line[:date_length], self.config["timeformat"] ) diff --git a/jrnl/__init__.py b/jrnl/__init__.py index 8f4dc3ec..550d580f 100644 --- a/jrnl/__init__.py +++ b/jrnl/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/__main__.py b/jrnl/__main__.py index e977369f..49497161 100644 --- a/jrnl/__main__.py +++ b/jrnl/__main__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/cli.py b/jrnl/cli.py index 93a7e899..6a1c6a0f 100644 --- a/jrnl/cli.py +++ b/jrnl/cli.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/color.py b/jrnl/color.py index 3bdd4149..691cce9c 100644 --- a/jrnl/color.py +++ b/jrnl/color.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python import re from string import punctuation from string import whitespace diff --git a/jrnl/config.py b/jrnl/config.py index da2df2cc..32695204 100644 --- a/jrnl/config.py +++ b/jrnl/config.py @@ -41,7 +41,7 @@ def make_yaml_valid_dict(input: list) -> dict: # yaml compatible strings are of the form Key:Value yamlstr = YAML_SEPARATOR.join(input) - runtime_modifications = yaml.load(yamlstr, Loader=yaml.FullLoader) + runtime_modifications = yaml.load(yamlstr, Loader=yaml.SafeLoader) return runtime_modifications @@ -140,7 +140,7 @@ def verify_config_colors(config): def load_config(config_path): """Tries to load a config file from YAML.""" with open(config_path) as f: - return yaml.load(f, Loader=yaml.FullLoader) + return yaml.load(f, Loader=yaml.SafeLoader) def is_config_json(config_path): diff --git a/jrnl/editor.py b/jrnl/editor.py index 086d84db..7c7413e8 100644 --- a/jrnl/editor.py +++ b/jrnl/editor.py @@ -26,7 +26,7 @@ def get_text_from_editor(config, template=""): try: subprocess.call(split_args(config["editor"]) + [tmpfile]) - except Exception as e: + except FileNotFoundError as e: error_msg = f""" {ERROR_COLOR}{str(e)}{RESET_COLOR} diff --git a/jrnl/exception.py b/jrnl/exception.py index cb6672d7..07bf9023 100644 --- a/jrnl/exception.py +++ b/jrnl/exception.py @@ -22,25 +22,24 @@ class JrnlError(Exception): def _get_error_message(self, **kwargs): error_messages = { - "ConfigDirectoryIsFile": textwrap.dedent( - """ + "ConfigDirectoryIsFile": """ The path to your jrnl configuration directory is a file, not a directory: {config_directory_path} Removing this file will allow jrnl to save its configuration. - """ - ), - "LineWrapTooSmallForDateFormat": textwrap.dedent( - """ - The provided linewrap value of {config_linewrap} is too small by {columns} columns - to display the timestamps in the configured time format for journal {journal}. + """, + "LineWrapTooSmallForDateFormat": """ + The provided linewrap value of {config_linewrap} is too small by + {columns} columns to display the timestamps in the configured time + format for journal {journal}. - You can avoid this error by specifying a linewrap value that is larger by at least {columns} in the configuration file or by using --config-override at the command line - """ - ), + You can avoid this error by specifying a linewrap value that is larger + by at least {columns} in the configuration file or by using + --config-override at the command line + """, } - return error_messages[self.error_type].format(**kwargs) - - pass + msg = error_messages[self.error_type].format(**kwargs) + msg = textwrap.dedent(msg) + return msg diff --git a/jrnl/install.py b/jrnl/install.py index db4c0fba..b0ae2aa1 100644 --- a/jrnl/install.py +++ b/jrnl/install.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/__init__.py b/jrnl/plugins/__init__.py index 3eb4d5a2..da6199fb 100644 --- a/jrnl/plugins/__init__.py +++ b/jrnl/plugins/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/dates_exporter.py b/jrnl/plugins/dates_exporter.py index d11e527c..e032b652 100644 --- a/jrnl/plugins/dates_exporter.py +++ b/jrnl/plugins/dates_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/fancy_exporter.py b/jrnl/plugins/fancy_exporter.py index 15efc19b..2cb27eca 100644 --- a/jrnl/plugins/fancy_exporter.py +++ b/jrnl/plugins/fancy_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/jrnl_importer.py b/jrnl/plugins/jrnl_importer.py index af5ea6ce..214fc70b 100644 --- a/jrnl/plugins/jrnl_importer.py +++ b/jrnl/plugins/jrnl_importer.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/json_exporter.py b/jrnl/plugins/json_exporter.py index dd07b0ce..666d9a3d 100644 --- a/jrnl/plugins/json_exporter.py +++ b/jrnl/plugins/json_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/markdown_exporter.py b/jrnl/plugins/markdown_exporter.py index 693f2fa5..11f748b6 100644 --- a/jrnl/plugins/markdown_exporter.py +++ b/jrnl/plugins/markdown_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/tag_exporter.py b/jrnl/plugins/tag_exporter.py index bc3736eb..1153fa01 100644 --- a/jrnl/plugins/tag_exporter.py +++ b/jrnl/plugins/tag_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/template.py b/jrnl/plugins/template.py index 147cd7af..cb852471 100644 --- a/jrnl/plugins/template.py +++ b/jrnl/plugins/template.py @@ -26,7 +26,7 @@ class Template: def from_file(cls, filename): with open(filename) as f: front_matter, body = f.read().strip("-\n").split("---", 2) - front_matter = yaml.load(front_matter, Loader=yaml.FullLoader) + front_matter = yaml.load(front_matter, Loader=yaml.SafeLoader) template = cls(body) template.__dict__.update(front_matter) return template diff --git a/jrnl/plugins/template_exporter.py b/jrnl/plugins/template_exporter.py index af081f8c..d2e5ce3e 100644 --- a/jrnl/plugins/template_exporter.py +++ b/jrnl/plugins/template_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/text_exporter.py b/jrnl/plugins/text_exporter.py index 7714606c..c9eaaf14 100644 --- a/jrnl/plugins/text_exporter.py +++ b/jrnl/plugins/text_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/util.py b/jrnl/plugins/util.py index 04159ca4..ae49a2a8 100644 --- a/jrnl/plugins/util.py +++ b/jrnl/plugins/util.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/xml_exporter.py b/jrnl/plugins/xml_exporter.py index b9467912..9901f4b7 100644 --- a/jrnl/plugins/xml_exporter.py +++ b/jrnl/plugins/xml_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/plugins/yaml_exporter.py b/jrnl/plugins/yaml_exporter.py index 7716c6c1..887fdaf1 100644 --- a/jrnl/plugins/yaml_exporter.py +++ b/jrnl/plugins/yaml_exporter.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # encoding: utf-8 # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/jrnl/time.py b/jrnl/time.py index b9ea8e84..f4e7319d 100644 --- a/jrnl/time.py +++ b/jrnl/time.py @@ -1,11 +1,11 @@ # Copyright (C) 2012-2021 jrnl contributors # License: https://www.gnu.org/licenses/gpl-3.0.html -from datetime import datetime +import datetime FAKE_YEAR = 9999 -DEFAULT_FUTURE = datetime(FAKE_YEAR, 12, 31, 23, 59, 59) -DEFAULT_PAST = datetime(FAKE_YEAR, 1, 1, 0, 0) +DEFAULT_FUTURE = datetime.datetime(FAKE_YEAR, 12, 31, 23, 59, 59) +DEFAULT_PAST = datetime.datetime(FAKE_YEAR, 1, 1, 0, 0) def __get_pdt_calendar(): @@ -27,7 +27,7 @@ def parse( """Parses a string containing a fuzzy date and returns a datetime.datetime object""" if not date_str: return None - elif isinstance(date_str, datetime): + elif isinstance(date_str, datetime.datetime): return date_str # Don't try to parse anything with 6 or less characters and was parsed from the existing journal. @@ -44,7 +44,9 @@ def parse( date = dateparse(date_str, default=default_date) if date.year == FAKE_YEAR: - date = datetime(datetime.now().year, date.timetuple()[1:6]) + date = datetime.datetime( + datetime.datetime.now().year, date.timetuple()[1:6] + ) else: year_present = True flag = 1 if date.hour == date.minute == 0 else 2 @@ -52,7 +54,7 @@ def parse( except Exception as e: if e.args[0] == "day is out of range for month": y, m, d, H, M, S = default_date.timetuple()[:6] - default_date = datetime(y, m, d - 1, H, M, S) + default_date = datetime.datetime(y, m, d - 1, H, M, S) else: calendar = __get_pdt_calendar() date, flag = calendar.parse(date_str) @@ -60,26 +62,26 @@ def parse( if not flag: # Oops, unparsable. try: # Try and parse this as a single year year = int(date_str) - return datetime(year, 1, 1) + return datetime.datetime(year, 1, 1) except ValueError: return None except TypeError: return None if flag == 1: # Date found, but no time. Use the default time. - date = datetime( + date = datetime.datetime( *date[:3], hour=23 if inclusive else default_hour or 0, minute=59 if inclusive else default_minute or 0, second=59 if inclusive else 0 ) else: - date = datetime(*date[:6]) + date = datetime.datetime(*date[:6]) # Ugly heuristic: if the date is more than 4 weeks in the future, we got the year wrong. # Rather then this, we would like to see parsedatetime patched so we can tell it to prefer # past dates - dt = datetime.now() - date + dt = datetime.datetime.now() - date if dt.days < -28 and not year_present: date = date.replace(date.year - 1) return date diff --git a/poetry.lock b/poetry.lock index 238b977c..94b251d3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,23 +17,31 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "appnope" +version = "0.1.2" +description = "Disable App Nap on macOS >= 10.9" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "argcomplete" -version = "1.12.2" +version = "1.12.3" description = "Bash tab completion for argparse" category = "dev" optional = false python-versions = "*" [package.dependencies] -importlib-metadata = {version = ">=0.23,<4", markers = "python_version == \"3.7\""} +importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} [package.extras] test = ["coverage", "flake8", "pexpect", "wheel"] [[package]] name = "asteval" -version = "0.9.23" +version = "0.9.25" description = "Safe, minimalistic evaluator of python expression using ast module" category = "main" optional = false @@ -43,29 +51,37 @@ python-versions = ">=3.6" name = "atomicwrites" version = "1.4.0" description = "Atomic file writes." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.3.0" +version = "21.2.0" description = "Classes Without Boilerplate" -category = "dev" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "dev" +optional = false +python-versions = "*" [[package]] name = "behave" version = "1.2.6" description = "behave is behaviour-driven development, Python style" -category = "dev" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" @@ -80,7 +96,7 @@ docs = ["sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6)"] [[package]] name = "black" -version = "21.5b2" +version = "21.7b0" description = "The uncompromising code formatter." category = "dev" optional = false @@ -92,7 +108,7 @@ click = ">=7.1.2" mypy-extensions = ">=0.4.3" pathspec = ">=0.8.1,<1" regex = ">=2020.1.8" -toml = ">=0.10.1" +tomli = ">=0.2.6,<2.0.0" typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""} typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} @@ -104,7 +120,7 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cffi" -version = "1.14.4" +version = "1.14.6" description = "Foreign Function Interface for Python calling C code." category = "main" optional = false @@ -115,11 +131,15 @@ pycparser = "*" [[package]] name = "click" -version = "7.1.2" +version = "8.0.1" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -129,6 +149,17 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + [[package]] name = "cryptography" version = "3.4.7" @@ -149,16 +180,52 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] [[package]] -name = "future" -version = "0.18.2" -description = "Clean single-source support for Python 3 and 2" +name = "decorator" +version = "5.0.9" +description = "Decorators for Humans" category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.5" + +[[package]] +name = "flake8" +version = "3.9.2" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" + +[[package]] +name = "ghp-import" +version = "2.0.1" +description = "Copy your docs directly to the gh-pages branch." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["twine", "markdown", "flake8"] + +[[package]] +name = "glob2" +version = "0.7" +description = "Version of the glob module that can capture patterns and supports recursive wildcards" +category = "main" +optional = false +python-versions = "*" [[package]] name = "importlib-metadata" -version = "3.7.2" +version = "4.6.1" description = "Read metadata from Python packages" category = "main" optional = false @@ -170,19 +237,88 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +perf = ["ipython"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "ipdb" +version = "0.13.9" +description = "IPython-enabled pdb" +category = "dev" +optional = false +python-versions = ">=2.7" + +[package.dependencies] +decorator = {version = "*", markers = "python_version > \"3.6\""} +ipython = {version = ">=7.17.0", markers = "python_version > \"3.6\""} +toml = {version = ">=0.10.2", markers = "python_version > \"3.6\""} + +[[package]] +name = "ipython" +version = "7.25.0" +description = "IPython: Productive Interactive Computing" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["notebook", "ipywidgets"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] + +[[package]] +name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" category = "dev" optional = false python-versions = "*" +[[package]] +name = "jedi" +version = "0.18.0" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] + [[package]] name = "jeepney" -version = "0.6.0" +version = "0.7.0" description = "Low-level, pure Python DBus protocol wrapper." category = "main" optional = false @@ -190,28 +326,21 @@ python-versions = ">=3.6" [package.extras] test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio"] +trio = ["trio", "async-generator"] [[package]] name = "jinja2" -version = "2.11.2" +version = "3.0.1" description = "A very fast and expressive template engine." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] -MarkupSafe = ">=0.23" +MarkupSafe = ">=2.0" [package.extras] -i18n = ["Babel (>=0.8)"] - -[[package]] -name = "joblib" -version = "0.17.0" -description = "Lightweight pipelining: using Python functions as pipeline jobs." -category = "dev" -optional = false -python-versions = ">=3.6" +i18n = ["Babel (>=2.7)"] [[package]] name = "keyring" @@ -232,36 +361,23 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] [[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -category = "dev" +name = "mako" +version = "1.1.4" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "lunr" -version = "0.5.8" -description = "A Python implementation of Lunr.js" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -future = ">=0.16.0" -nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} -six = ">=1.11.0" +MarkupSafe = ">=0.9.2" [package.extras] -languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] +babel = ["babel"] +lingua = ["lingua"] [[package]] name = "markdown" -version = "3.3.3" +version = "3.3.4" description = "Python implementation of Markdown." category = "dev" optional = false @@ -275,28 +391,61 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "1.1.1" +version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" +category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.6" [[package]] -name = "mkdocs" -version = "1.1.2" -description = "Project documentation with Markdown." +name = "matplotlib-inline" +version = "0.1.2" +description = "Inline Matplotlib backend for Jupyter" category = "dev" optional = false python-versions = ">=3.5" +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "mkdocs" +version = "1.2.1" +description = "Project documentation with Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" + [package.dependencies] click = ">=3.3" +ghp-import = ">=1.0" +importlib-metadata = ">=3.10" Jinja2 = ">=2.10.1" -livereload = ">=2.5.1" -lunr = {version = "0.5.8", extras = ["languages"]} Markdown = ">=3.2.1" +mergedeep = ">=1.3.4" +packaging = ">=20.5" PyYAML = ">=3.10" -tornado = ">=5.0" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] [[package]] name = "mypy-extensions" @@ -306,44 +455,22 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "nltk" -version = "3.5" -description = "Natural Language Toolkit" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -click = "*" -joblib = "*" -regex = "*" -tqdm = "*" - -[package.extras] -all = ["requests", "numpy", "python-crfsuite", "scikit-learn", "twython", "pyparsing", "scipy", "matplotlib", "gensim"] -corenlp = ["requests"] -machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"] -plot = ["matplotlib"] -tgrep = ["pyparsing"] -twitter = ["twython"] - [[package]] name = "packaging" -version = "20.8" +version = "21.0" description = "Core utilities for Python packages" -category = "dev" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2" [[package]] name = "parse" -version = "1.18.0" +version = "1.19.0" description = "parse() is the opposite of format()" -category = "dev" +category = "main" optional = false python-versions = "*" @@ -351,7 +478,7 @@ python-versions = "*" name = "parse-type" version = "0.5.2" description = "Simplifies to build parse types based on the parse module" -category = "dev" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" @@ -371,6 +498,18 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "parso" +version = "0.8.2" +description = "A Python Parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + [[package]] name = "pathspec" version = "0.8.1" @@ -379,11 +518,30 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "pluggy" version = "0.13.1" description = "plugin and hook calling mechanisms for python" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" @@ -393,10 +551,45 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] +[[package]] +name = "pprintpp" +version = "0.4.0" +description = "A drop-in replacement for pprint that's actually pretty" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "prompt-toolkit" +version = "3.0.19" +description = "Library for building powerful interactive command lines in Python" +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "py" version = "1.10.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" @@ -417,19 +610,39 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pygments" +version = "2.9.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.5" + [[package]] name = "pyparsing" version = "2.4.7" description = "Python parsing module" -category = "dev" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "pyproject-flake8" +version = "0.0.1a2" +description = "pyproject-flake8 (`pflake8`), a monkey patching wrapper to connect flake8 with pyproject.toml configuration" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = "*" +toml = "*" + [[package]] name = "pytest" version = "6.2.4" description = "pytest: simple powerful testing with Python" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -447,9 +660,38 @@ toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[[package]] +name = "pytest-bdd" +version = "4.1.0" +description = "BDD for pytest" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +glob2 = "*" +Mako = "*" +parse = "*" +parse-type = "*" +py = "*" +pytest = ">=4.3" + +[[package]] +name = "pytest-clarity" +version = "1.0.1" +description = "A plugin providing an alternative, colourful diff output for failing assertions." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pprintpp = ">=0.4.0" +pytest = ">=3.5.0" +rich = ">=8.0.0" + [[package]] name = "python-dateutil" -version = "2.8.1" +version = "2.8.2" description = "Extensions to the standard Python datetime module" category = "main" optional = false @@ -490,17 +732,45 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyyaml = "*" + [[package]] name = "regex" -version = "2020.11.13" +version = "2021.7.6" description = "Alternative regular expression module, to replace re." category = "dev" optional = false python-versions = "*" +[[package]] +name = "rich" +version = "10.6.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +colorama = ">=0.4.0,<0.5.0" +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" +typing-extensions = {version = ">=3.7.4,<4.0.0", markers = "python_version < \"3.8\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + [[package]] name = "secretstorage" -version = "3.3.0" +version = "3.3.1" description = "Python bindings to FreeDesktop.org Secret Service API" category = "main" optional = false @@ -512,7 +782,7 @@ jeepney = ">=0.6" [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -530,28 +800,31 @@ python-versions = "*" name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] -name = "tornado" -version = "6.1" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +name = "tomli" +version = "1.0.4" +description = "A lil' TOML parser" category = "dev" optional = false -python-versions = ">= 3.5" +python-versions = ">=3.6" [[package]] -name = "tqdm" -version = "4.54.1" -description = "Fast, Extensible Progress Meter" +name = "traitlets" +version = "5.0.5" +description = "Traitlets Python configuration system" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = ">=3.7" + +[package.dependencies] +ipython-genutils = "*" [package.extras] -dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown", "wheel"] +test = ["pytest"] [[package]] name = "typed-ast" @@ -563,7 +836,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "3.7.4.3" +version = "3.10.0.0" description = "Backported and Experimental Type Hints for Python 3.5+" category = "main" optional = false @@ -580,6 +853,25 @@ python-versions = "*" [package.dependencies] pytz = "*" +[[package]] +name = "watchdog" +version = "2.1.3" +description = "Filesystem events monitoring" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchmedo = ["PyYAML (>=3.10)", "argh (>=0.24.1)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "xmltodict" version = "0.12.0" @@ -590,7 +882,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "yq" -version = "2.12.0" +version = "2.12.2" description = "Command-line YAML/XML processor - jq wrapper for YAML/XML documents" category = "dev" optional = false @@ -607,20 +899,23 @@ test = ["coverage", "flake8", "wheel"] [[package]] name = "zipp" -version = "3.4.0" +version = "3.5.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.6" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[extras] +testing = ["behave", "pytest", "pytest-bdd", "toml"] [metadata] lock-version = "1.1" -python-versions = ">=3.7.0, <3.10" -content-hash = "4d9054a240b279da990b8038623d8b2025487ef37162f7f7ef4a4ff8cbae872b" +python-versions = ">=3.7.0, <3.11" +content-hash = "c4f943f3dbd96f5aab8ad0ef2f3d3f6a7096dc75ea7d3e449949695d340208fb" [metadata.files] ansiwrap = [ @@ -631,76 +926,96 @@ appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] +appnope = [ + {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, + {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, +] argcomplete = [ - {file = "argcomplete-1.12.2-py2.py3-none-any.whl", hash = "sha256:17f01a9b9b9ece3e6b07058eae737ad6e10de8b4e149105f84614783913aba71"}, - {file = "argcomplete-1.12.2.tar.gz", hash = "sha256:de0e1282330940d52ea92a80fea2e4b9e0da1932aaa570f84d268939d1897b04"}, + {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, + {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, ] asteval = [ - {file = "asteval-0.9.23.tar.gz", hash = "sha256:f5096a924b1d2f147e70327245d95fc8f534dbe94277b6828ce2a8c049d3a438"}, + {file = "asteval-0.9.25.tar.gz", hash = "sha256:bea22b7d8fa16bcba95ebc72052ae5d8ca97114c9959bb47f8b8eebf30e4342f"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, +] +backcall = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] behave = [ {file = "behave-1.2.6-py2.py3-none-any.whl", hash = "sha256:ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c"}, {file = "behave-1.2.6.tar.gz", hash = "sha256:b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86"}, ] black = [ - {file = "black-21.5b2-py3-none-any.whl", hash = "sha256:e5cf21ebdffc7a9b29d73912b6a6a9a4df4ce70220d523c21647da2eae0751ef"}, - {file = "black-21.5b2.tar.gz", hash = "sha256:1fc0e0a2c8ae7d269dfcf0c60a89afa299664f3e811395d40b1922dff8f854b5"}, + {file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"}, + {file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"}, ] cffi = [ - {file = "cffi-1.14.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775"}, - {file = "cffi-1.14.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06"}, - {file = "cffi-1.14.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26"}, - {file = "cffi-1.14.4-cp27-cp27m-win32.whl", hash = "sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c"}, - {file = "cffi-1.14.4-cp27-cp27m-win_amd64.whl", hash = "sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b"}, - {file = "cffi-1.14.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d"}, - {file = "cffi-1.14.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca"}, - {file = "cffi-1.14.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698"}, - {file = "cffi-1.14.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b"}, - {file = "cffi-1.14.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293"}, - {file = "cffi-1.14.4-cp35-cp35m-win32.whl", hash = "sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2"}, - {file = "cffi-1.14.4-cp35-cp35m-win_amd64.whl", hash = "sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7"}, - {file = "cffi-1.14.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f"}, - {file = "cffi-1.14.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362"}, - {file = "cffi-1.14.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec"}, - {file = "cffi-1.14.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b"}, - {file = "cffi-1.14.4-cp36-cp36m-win32.whl", hash = "sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668"}, - {file = "cffi-1.14.4-cp36-cp36m-win_amd64.whl", hash = "sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009"}, - {file = "cffi-1.14.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb"}, - {file = "cffi-1.14.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d"}, - {file = "cffi-1.14.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03"}, - {file = "cffi-1.14.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01"}, - {file = "cffi-1.14.4-cp37-cp37m-win32.whl", hash = "sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e"}, - {file = "cffi-1.14.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35"}, - {file = "cffi-1.14.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d"}, - {file = "cffi-1.14.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b"}, - {file = "cffi-1.14.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53"}, - {file = "cffi-1.14.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e"}, - {file = "cffi-1.14.4-cp38-cp38-win32.whl", hash = "sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d"}, - {file = "cffi-1.14.4-cp38-cp38-win_amd64.whl", hash = "sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375"}, - {file = "cffi-1.14.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909"}, - {file = "cffi-1.14.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd"}, - {file = "cffi-1.14.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a"}, - {file = "cffi-1.14.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7ef7d4ced6b325e92eb4d3502946c78c5367bc416398d387b39591532536734e"}, - {file = "cffi-1.14.4-cp39-cp39-win32.whl", hash = "sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3"}, - {file = "cffi-1.14.4-cp39-cp39-win_amd64.whl", hash = "sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b"}, - {file = "cffi-1.14.4.tar.gz", hash = "sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c"}, + {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"}, + {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"}, + {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"}, + {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"}, + {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, + {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, + {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, + {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, + {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, + {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, + {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"}, + {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"}, + {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"}, + {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"}, + {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"}, + {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"}, + {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"}, + {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"}, + {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"}, + {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"}, + {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"}, + {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"}, + {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, + {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] +commonmark = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] cryptography = [ {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"}, {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"}, @@ -715,115 +1030,125 @@ cryptography = [ {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"}, {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, ] -future = [ - {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +decorator = [ + {file = "decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323"}, + {file = "decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5"}, +] +flake8 = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] +ghp-import = [ + {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"}, +] +glob2 = [ + {file = "glob2-0.7.tar.gz", hash = "sha256:85c3dbd07c8aa26d63d7aacee34fa86e9a91a3873bc30bf62ec46e531f92ab8c"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.7.2-py3-none-any.whl", hash = "sha256:407d13f55dc6f2a844e62325d18ad7019a436c4bfcaee34cda35f2be6e7c3e34"}, - {file = "importlib_metadata-3.7.2.tar.gz", hash = "sha256:18d5ff601069f98d5d605b6a4b50c18a34811d655c55548adc833e687289acde"}, + {file = "importlib_metadata-4.6.1-py3-none-any.whl", hash = "sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e"}, + {file = "importlib_metadata-4.6.1.tar.gz", hash = "sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] +ipdb = [ + {file = "ipdb-0.13.9.tar.gz", hash = "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5"}, +] +ipython = [ + {file = "ipython-7.25.0-py3-none-any.whl", hash = "sha256:aa21412f2b04ad1a652e30564fff6b4de04726ce875eab222c8430edc6db383a"}, + {file = "ipython-7.25.0.tar.gz", hash = "sha256:54bbd1fe3882457aaf28ae060a5ccdef97f212a741754e420028d4ec5c2291dc"}, +] +ipython-genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] +jedi = [ + {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, + {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, +] jeepney = [ - {file = "jeepney-0.6.0-py3-none-any.whl", hash = "sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae"}, - {file = "jeepney-0.6.0.tar.gz", hash = "sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657"}, + {file = "jeepney-0.7.0-py3-none-any.whl", hash = "sha256:71335e7a4e93817982f473f3507bffc2eff7a544119ab9b73e089c8ba1409ba3"}, + {file = "jeepney-0.7.0.tar.gz", hash = "sha256:1237cd64c8f7ac3aa4b3f332c4d0fb4a8216f39eaa662ec904302d4d77de5a54"}, ] jinja2 = [ - {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, - {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, -] -joblib = [ - {file = "joblib-0.17.0-py3-none-any.whl", hash = "sha256:698c311779f347cf6b7e6b8a39bb682277b8ee4aba8cf9507bc0cf4cd4737b72"}, - {file = "joblib-0.17.0.tar.gz", hash = "sha256:9e284edd6be6b71883a63c9b7f124738a3c16195513ad940eae7e3438de885d5"}, + {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, + {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, ] keyring = [ {file = "keyring-23.0.1-py3-none-any.whl", hash = "sha256:8f607d7d1cc502c43a932a275a56fe47db50271904513a379d39df1af277ac48"}, {file = "keyring-23.0.1.tar.gz", hash = "sha256:045703609dd3fccfcdb27da201684278823b72af515aedec1a8515719a038cb8"}, ] -livereload = [ - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] -lunr = [ - {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, - {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, +mako = [ + {file = "Mako-1.1.4-py2.py3-none-any.whl", hash = "sha256:aea166356da44b9b830c8023cd9b557fa856bd8b4035d6de771ca027dfc5cc6e"}, + {file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"}, ] markdown = [ - {file = "Markdown-3.3.3-py3-none-any.whl", hash = "sha256:c109c15b7dc20a9ac454c9e6025927d44460b85bd039da028d85e2b6d0bcc328"}, - {file = "Markdown-3.3.3.tar.gz", hash = "sha256:5d9f2b5ca24bc4c7a390d22323ca4bad200368612b5aaa7796babf971d2b2f18"}, + {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, + {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, ] markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, - {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, +] +matplotlib-inline = [ + {file = "matplotlib-inline-0.1.2.tar.gz", hash = "sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"}, + {file = "matplotlib_inline-0.1.2-py3-none-any.whl", hash = "sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mergedeep = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, ] mkdocs = [ - {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, - {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, + {file = "mkdocs-1.2.1-py3-none-any.whl", hash = "sha256:11141126e5896dd9d279b3e4814eb488e409a0990fb638856255020406a8e2e7"}, + {file = "mkdocs-1.2.1.tar.gz", hash = "sha256:6e0ea175366e3a50d334597b0bc042b8cebd512398cdd3f6f34842d0ef524905"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] -nltk = [ - {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, -] packaging = [ - {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, - {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, + {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, + {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, ] parse = [ - {file = "parse-1.18.0.tar.gz", hash = "sha256:91666032d6723dc5905248417ef0dc9e4c51df9526aaeef271eacad6491f06a4"}, + {file = "parse-1.19.0.tar.gz", hash = "sha256:9ff82852bcb65d139813e2a5197627a94966245c897796760a3a2a8eb66f020b"}, ] parse-type = [ {file = "parse_type-0.5.2-py2.py3-none-any.whl", hash = "sha256:089a471b06327103865dfec2dd844230c3c658a4a1b5b4c8b6c16c8f77577f9e"}, @@ -833,18 +1158,46 @@ parsedatetime = [ {file = "parsedatetime-2.6-py3-none-any.whl", hash = "sha256:cb96edd7016872f58479e35879294258c71437195760746faffedb692aef000b"}, {file = "parsedatetime-2.6.tar.gz", hash = "sha256:4cb368fbb18a0b7231f4d76119165451c8d2e35951455dfee97c62a87b04d455"}, ] +parso = [ + {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, + {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, +] pathspec = [ {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, ] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] +pprintpp = [ + {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, + {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"}, + {file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"}, +] +ptyprocess = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] +pycodestyle = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, @@ -853,17 +1206,32 @@ pyflakes = [ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] +pygments = [ + {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, + {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"}, +] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] +pyproject-flake8 = [ + {file = "pyproject-flake8-0.0.1a2.tar.gz", hash = "sha256:bdeca37f78ecd34bd64a49d3657d53d099f5445831071a31c46e1fe20cd61461"}, + {file = "pyproject_flake8-0.0.1a2-py2.py3-none-any.whl", hash = "sha256:e61ed1dc088e9f9f8a7170967ac4ec135acfef3a59ab9738c7b58cc11f294a7e"}, +] pytest = [ {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, ] +pytest-bdd = [ + {file = "pytest-bdd-4.1.0.tar.gz", hash = "sha256:304cd2b09923b838d0c2f08331d1f4236a14ef3594efa94e3bdae0f384d3fa5d"}, + {file = "pytest_bdd-4.1.0-py3-none-any.whl", hash = "sha256:7c5221680cec9a97630e1fae6132f4a97c2f86a90914206ee06a55ae1a409fe5"}, +] +pytest-clarity = [ + {file = "pytest-clarity-1.0.1.tar.gz", hash = "sha256:505fe345fad4fe11c6a4187fe683f2c7c52c077caa1e135f3e483fe112db7772"}, +] python-dateutil = [ - {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] pytz = [ {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, @@ -908,56 +1276,64 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] +pyyaml-env-tag = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] regex = [ - {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, - {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, - {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, - {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, - {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, - {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, - {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, - {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, - {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, - {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, - {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, - {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, - {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, + {file = "regex-2021.7.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e6a1e5ca97d411a461041d057348e578dc344ecd2add3555aedba3b408c9f874"}, + {file = "regex-2021.7.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6afe6a627888c9a6cfbb603d1d017ce204cebd589d66e0703309b8048c3b0854"}, + {file = "regex-2021.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ccb3d2190476d00414aab36cca453e4596e8f70a206e2aa8db3d495a109153d2"}, + {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:ed693137a9187052fc46eedfafdcb74e09917166362af4cc4fddc3b31560e93d"}, + {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99d8ab206a5270c1002bfcf25c51bf329ca951e5a169f3b43214fdda1f0b5f0d"}, + {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:b85ac458354165405c8a84725de7bbd07b00d9f72c31a60ffbf96bb38d3e25fa"}, + {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3f5716923d3d0bfb27048242a6e0f14eecdb2e2a7fac47eda1d055288595f222"}, + {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5983c19d0beb6af88cb4d47afb92d96751fb3fa1784d8785b1cdf14c6519407"}, + {file = "regex-2021.7.6-cp36-cp36m-win32.whl", hash = "sha256:c92831dac113a6e0ab28bc98f33781383fe294df1a2c3dfd1e850114da35fd5b"}, + {file = "regex-2021.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:791aa1b300e5b6e5d597c37c346fb4d66422178566bbb426dd87eaae475053fb"}, + {file = "regex-2021.7.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59506c6e8bd9306cd8a41511e32d16d5d1194110b8cfe5a11d102d8b63cf945d"}, + {file = "regex-2021.7.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:564a4c8a29435d1f2256ba247a0315325ea63335508ad8ed938a4f14c4116a5d"}, + {file = "regex-2021.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:59c00bb8dd8775473cbfb967925ad2c3ecc8886b3b2d0c90a8e2707e06c743f0"}, + {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9a854b916806c7e3b40e6616ac9e85d3cdb7649d9e6590653deb5b341a736cec"}, + {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:db2b7df831c3187a37f3bb80ec095f249fa276dbe09abd3d35297fc250385694"}, + {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:173bc44ff95bc1e96398c38f3629d86fa72e539c79900283afa895694229fe6a"}, + {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:15dddb19823f5147e7517bb12635b3c82e6f2a3a6b696cc3e321522e8b9308ad"}, + {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ddeabc7652024803666ea09f32dd1ed40a0579b6fbb2a213eba590683025895"}, + {file = "regex-2021.7.6-cp37-cp37m-win32.whl", hash = "sha256:f080248b3e029d052bf74a897b9d74cfb7643537fbde97fe8225a6467fb559b5"}, + {file = "regex-2021.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d8bbce0c96462dbceaa7ac4a7dfbbee92745b801b24bce10a98d2f2b1ea9432f"}, + {file = "regex-2021.7.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edd1a68f79b89b0c57339bce297ad5d5ffcc6ae7e1afdb10f1947706ed066c9c"}, + {file = "regex-2021.7.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:422dec1e7cbb2efbbe50e3f1de36b82906def93ed48da12d1714cabcd993d7f0"}, + {file = "regex-2021.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cbe23b323988a04c3e5b0c387fe3f8f363bf06c0680daf775875d979e376bd26"}, + {file = "regex-2021.7.6-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:0eb2c6e0fcec5e0f1d3bcc1133556563222a2ffd2211945d7b1480c1b1a42a6f"}, + {file = "regex-2021.7.6-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1c78780bf46d620ff4fff40728f98b8afd8b8e35c3efd638c7df67be2d5cddbf"}, + {file = "regex-2021.7.6-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bc84fb254a875a9f66616ed4538542fb7965db6356f3df571d783f7c8d256edd"}, + {file = "regex-2021.7.6-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:598c0a79b4b851b922f504f9f39a863d83ebdfff787261a5ed061c21e67dd761"}, + {file = "regex-2021.7.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875c355360d0f8d3d827e462b29ea7682bf52327d500a4f837e934e9e4656068"}, + {file = "regex-2021.7.6-cp38-cp38-win32.whl", hash = "sha256:e586f448df2bbc37dfadccdb7ccd125c62b4348cb90c10840d695592aa1b29e0"}, + {file = "regex-2021.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:2fe5e71e11a54e3355fa272137d521a40aace5d937d08b494bed4529964c19c4"}, + {file = "regex-2021.7.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6110bab7eab6566492618540c70edd4d2a18f40ca1d51d704f1d81c52d245026"}, + {file = "regex-2021.7.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4f64fc59fd5b10557f6cd0937e1597af022ad9b27d454e182485f1db3008f417"}, + {file = "regex-2021.7.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:89e5528803566af4df368df2d6f503c84fbfb8249e6631c7b025fe23e6bd0cde"}, + {file = "regex-2021.7.6-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2366fe0479ca0e9afa534174faa2beae87847d208d457d200183f28c74eaea59"}, + {file = "regex-2021.7.6-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f9392a4555f3e4cb45310a65b403d86b589adc773898c25a39184b1ba4db8985"}, + {file = "regex-2021.7.6-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:2bceeb491b38225b1fee4517107b8491ba54fba77cf22a12e996d96a3c55613d"}, + {file = "regex-2021.7.6-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:f98dc35ab9a749276f1a4a38ab3e0e2ba1662ce710f6530f5b0a6656f1c32b58"}, + {file = "regex-2021.7.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:319eb2a8d0888fa6f1d9177705f341bc9455a2c8aca130016e52c7fe8d6c37a3"}, + {file = "regex-2021.7.6-cp39-cp39-win32.whl", hash = "sha256:eaf58b9e30e0e546cdc3ac06cf9165a1ca5b3de8221e9df679416ca667972035"}, + {file = "regex-2021.7.6-cp39-cp39-win_amd64.whl", hash = "sha256:4c9c3155fe74269f61e27617529b7f09552fbb12e44b1189cebbdb24294e6e1c"}, + {file = "regex-2021.7.6.tar.gz", hash = "sha256:8394e266005f2d8c6f0bc6780001f7afa3ef81a7a2111fa35058ded6fce79e4d"}, +] +rich = [ + {file = "rich-10.6.0-py3-none-any.whl", hash = "sha256:d3f72827cd5df13b2ef7f1a97f81ec65548d4fdeb92cef653234f227580bbb2a"}, + {file = "rich-10.6.0.tar.gz", hash = "sha256:128261b3e2419a4ef9c97066ccc2abbfb49fa7c5e89c3fe4056d00aa5e9c1e65"}, ] secretstorage = [ - {file = "SecretStorage-3.3.0-py3-none-any.whl", hash = "sha256:5c36f6537a523ec5f969ef9fad61c98eb9e017bc601d811e53aa25bece64892f"}, - {file = "SecretStorage-3.3.0.tar.gz", hash = "sha256:30cfdef28829dad64d6ea1ed08f8eff6aa115a77068926bcc9f5225d5a3246aa"}, + {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"}, + {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] textwrap3 = [ {file = "textwrap3-0.9.2-py2.py3-none-any.whl", hash = "sha256:bf5f4c40faf2a9ff00a9e0791fed5da7415481054cef45bb4a3cfb1f69044ae0"}, @@ -967,52 +1343,13 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -tornado = [ - {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, - {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, - {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, - {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, - {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, - {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, - {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, - {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, - {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, - {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, - {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, - {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, - {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, - {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, - {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, - {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, - {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, - {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, +tomli = [ + {file = "tomli-1.0.4-py3-none-any.whl", hash = "sha256:0713b16ff91df8638a6a694e295c8159ab35ba93e3424a626dd5226d386057be"}, + {file = "tomli-1.0.4.tar.gz", hash = "sha256:be670d0d8d7570fd0ea0113bd7bb1ba3ac6706b4de062cc4c952769355c9c268"}, ] -tqdm = [ - {file = "tqdm-4.54.1-py2.py3-none-any.whl", hash = "sha256:d4f413aecb61c9779888c64ddf0c62910ad56dcbe857d8922bb505d4dbff0df1"}, - {file = "tqdm-4.54.1.tar.gz", hash = "sha256:38b658a3e4ecf9b4f6f8ff75ca16221ae3378b2e175d846b6b33ea3a20852cf5"}, +traitlets = [ + {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, + {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, ] typed-ast = [ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, @@ -1047,23 +1384,50 @@ typed-ast = [ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] tzlocal = [ {file = "tzlocal-2.1-py2.py3-none-any.whl", hash = "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"}, {file = "tzlocal-2.1.tar.gz", hash = "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44"}, ] +watchdog = [ + {file = "watchdog-2.1.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9628f3f85375a17614a2ab5eac7665f7f7be8b6b0a2a228e6f6a2e91dd4bfe26"}, + {file = "watchdog-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:acc4e2d5be6f140f02ee8590e51c002829e2c33ee199036fcd61311d558d89f4"}, + {file = "watchdog-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:85b851237cf3533fabbc034ffcd84d0fa52014b3121454e5f8b86974b531560c"}, + {file = "watchdog-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a12539ecf2478a94e4ba4d13476bb2c7a2e0a2080af2bb37df84d88b1b01358a"}, + {file = "watchdog-2.1.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6fe9c8533e955c6589cfea6f3f0a1a95fb16867a211125236c82e1815932b5d7"}, + {file = "watchdog-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d9456f0433845e7153b102fffeb767bde2406b76042f2216838af3b21707894e"}, + {file = "watchdog-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd8c595d5a93abd441ee7c5bb3ff0d7170e79031520d113d6f401d0cf49d7c8f"}, + {file = "watchdog-2.1.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0bcfe904c7d404eb6905f7106c54873503b442e8e918cc226e1828f498bdc0ca"}, + {file = "watchdog-2.1.3-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bf84bd94cbaad8f6b9cbaeef43080920f4cb0e61ad90af7106b3de402f5fe127"}, + {file = "watchdog-2.1.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b8ddb2c9f92e0c686ea77341dcb58216fa5ff7d5f992c7278ee8a392a06e86bb"}, + {file = "watchdog-2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8805a5f468862daf1e4f4447b0ccf3acaff626eaa57fbb46d7960d1cf09f2e6d"}, + {file = "watchdog-2.1.3-py3-none-manylinux2014_armv7l.whl", hash = "sha256:3e305ea2757f81d8ebd8559d1a944ed83e3ab1bdf68bcf16ec851b97c08dc035"}, + {file = "watchdog-2.1.3-py3-none-manylinux2014_i686.whl", hash = "sha256:431a3ea70b20962e6dee65f0eeecd768cd3085ea613ccb9b53c8969de9f6ebd2"}, + {file = "watchdog-2.1.3-py3-none-manylinux2014_ppc64.whl", hash = "sha256:e4929ac2aaa2e4f1a30a36751160be391911da463a8799460340901517298b13"}, + {file = "watchdog-2.1.3-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:201cadf0b8c11922f54ec97482f95b2aafca429c4c3a4bb869a14f3c20c32686"}, + {file = "watchdog-2.1.3-py3-none-manylinux2014_s390x.whl", hash = "sha256:3a7d242a7963174684206093846537220ee37ba9986b824a326a8bb4ef329a33"}, + {file = "watchdog-2.1.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:54e057727dd18bd01a3060dbf5104eb5a495ca26316487e0f32a394fd5fe725a"}, + {file = "watchdog-2.1.3-py3-none-win32.whl", hash = "sha256:b5fc5c127bad6983eecf1ad117ab3418949f18af9c8758bd10158be3647298a9"}, + {file = "watchdog-2.1.3-py3-none-win_amd64.whl", hash = "sha256:44acad6f642996a2b50bb9ce4fb3730dde08f23e79e20cd3d8e2a2076b730381"}, + {file = "watchdog-2.1.3-py3-none-win_ia64.whl", hash = "sha256:0bcdf7b99b56a3ae069866c33d247c9994ffde91b620eaf0306b27e099bd1ae0"}, + {file = "watchdog-2.1.3.tar.gz", hash = "sha256:e5236a8e8602ab6db4b873664c2d356c365ab3cac96fbdec4970ad616415dd45"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] xmltodict = [ {file = "xmltodict-0.12.0-py2.py3-none-any.whl", hash = "sha256:8bbcb45cc982f48b2ca8fe7e7827c5d792f217ecf1792626f808bf41c3b86051"}, {file = "xmltodict-0.12.0.tar.gz", hash = "sha256:50d8c638ed7ecb88d90561beedbf720c9b4e851a9fa6c47ebd64e99d166d8a21"}, ] yq = [ - {file = "yq-2.12.0-py2.py3-none-any.whl", hash = "sha256:1f124f48dee77ad5e0be8607777fed183e96c8d31fa577de14201c7a614e4819"}, - {file = "yq-2.12.0.tar.gz", hash = "sha256:1d2ad403504d306b5258b86c698f9856d7ad58b7bb17a2b875691a6a7b8c4c20"}, + {file = "yq-2.12.2-py2.py3-none-any.whl", hash = "sha256:9fdf4487a6dbf985ca1d357ec93f926d982813e8e896e8892bae95162b6defe3"}, + {file = "yq-2.12.2.tar.gz", hash = "sha256:2f156d0724b61487ac8752ed4eaa702a5737b804d5afa46fa55866951cd106d2"}, ] zipp = [ - {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, - {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, + {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, + {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, ] diff --git a/pyproject.toml b/pyproject.toml index 8a46fe34..5c6d5cbd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ classifiers = [ "Funding" = "https://opencollective.com/jrnl" [tool.poetry.dependencies] -python = ">=3.7.0, <3.10" +python = ">=3.7.0, <3.11" ansiwrap = "^0.8.4" asteval = "^0.9" @@ -43,14 +43,29 @@ pyyaml = ">=5.1" pytz = ">=2020" # https://pythonhosted.org/pytz/#issues-limitations tzlocal = ">2.0, <3.0" # https://github.com/regebro/tzlocal/blob/master/CHANGES.txt +# Minimal deps required for testing +# I don't like repeating deps here, but +# there's no other way to do this yet until poetry v1.2 releases +# see: https://github.com/python-poetry/poetry/issues/1644 +behave = { version = "^1.2" , optional = true } +pytest = { version = ">=6.2" , optional = true } +pytest-bdd = { version = ">=4.0.1" , optional = true } +toml = { version = ">=0.10" , optional = true } + [tool.poetry.dev-dependencies] behave = "^1.2" -mkdocs = "^1.0" -black = {version = "^21.5b2",allow-prereleases = true} +mkdocs = ">=1.0" +black = { version = ">=21.5b2", allow-prereleases = true } toml = ">=0.10" -pyflakes = ">=2.2.0" pytest = ">=6.2" -yq = ">=2.11" +pytest-bdd = ">=4.0.1" +ipdb = "*" +pytest-clarity = "*" +pyproject-flake8 = "*" +yq = "*" + +[tool.poetry.extras] +testing = [ "behave", "pytest", "pytest-bdd", "toml" ] [tool.poetry.scripts] jrnl = 'jrnl.cli:cli' @@ -62,6 +77,29 @@ line_length = 88 known_first_party = ["jrnl"] force_sort_within_sections = true +[tool.pytest.ini_options] +minversion = "6.0" +required_plugins = [ + "pytest-bdd" +] +markers = [ + "todo", +] +addopts = [ + "--pdbcls=IPython.terminal.debugger:Pdb" +] + +filterwarnings = [ + "ignore::DeprecationWarning", + "ignore:Flag style will be deprecated in.*", + "ignore:[WinError 32].*", + "ignore:[WinError 5].*" +] + +[tool.flake8] +# ignore formatting warnings and errors because we use Black to autoformat +extend-ignore = "E101,E111,E114,E115,E116,E117,E12,E13,E2,E3,E401,E5,E70,W1,W2,W3,W5" + [build-system] requires = ["poetry>=1.1"] build-backend = "poetry.masonry.api" diff --git a/tests/bdd/features/build.feature b/tests/bdd/features/build.feature new file mode 100644 index 00000000..e9b47b49 --- /dev/null +++ b/tests/bdd/features/build.feature @@ -0,0 +1,7 @@ +Feature: Build process + + 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/tests/bdd/features/core.feature b/tests/bdd/features/core.feature new file mode 100644 index 00000000..4399341d --- /dev/null +++ b/tests/bdd/features/core.feature @@ -0,0 +1,18 @@ +Feature: Functionality of jrnl outside of actually handling journals + + Scenario: Displaying the version number + Given we use the config "simple.yaml" + When we run "jrnl --version" + Then we should get no error + Then the output should match "^jrnl version v\d+\.\d+(\.\d+)?(-(alpha|beta)\d*)?" + + Scenario: Running the diagnostic command + Given we use the config "simple.yaml" + When we run "jrnl --diagnostic" + Then the output should contain "jrnl" + And the output should contain "Python" + And the output should contain "OS" + + @todo + Scenario: Listing available journals + diff --git a/tests/bdd/features/datetime.feature b/tests/bdd/features/datetime.feature new file mode 100644 index 00000000..0da3027f --- /dev/null +++ b/tests/bdd/features/datetime.feature @@ -0,0 +1,174 @@ +Feature: Reading and writing to journal with custom date formats + + Scenario: Dates can include a time + # https://github.com/jrnl-org/jrnl/issues/117 + Given we use the config "simple.yaml" + When we run "jrnl 2013-11-30 15:42: Project Started." + Then we should see the message "Entry added" + When we run "jrnl -999" + Then the output should contain "2013-11-30 15:42 Project Started." + + + Scenario: Dates can be in the future + # https://github.com/jrnl-org/jrnl/issues/185 + Given we use the config "simple.yaml" + When we run "jrnl 26/06/2099: Planet? Earth. Year? 2099." + Then we should see the message "Entry added" + When we run "jrnl -999" + Then the output should contain "2099-06-26 09:00 Planet?" + + + Scenario: Loading a sample journal with custom date + Given we use the config "little_endian_dates.yaml" + When we run "jrnl -n 2" + Then we should get no error + When we run "jrnl -n 999" + Then the output should be + 09.06.2013 15:39 My first entry. + | Everything is alright + + 10.07.2013 15:40 Life is good. + | But I'm better. + + + Scenario Outline: Writing an entry from command line with custom date + Given we use the config "" + 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_file | command | expected_output | + | little_endian_dates.yaml | 2020-09-19: My first entry. | 19.09.2020 09:00 My first entry. | + | little_endian_dates.yaml | 2020-08-09: My second entry. | 09.08.2020 09:00 My second entry. | + | little_endian_dates.yaml | 2020-02-29: Test. | 29.02.2020 09:00 Test. | + | little_endian_dates.yaml | 2019-02-29: Test. | 2019-02-29: Test. | + | little_endian_dates.yaml | 2020-08-32: Test. | 2020-08-32: Test. | + | little_endian_dates.yaml | 2032-02-01: Test. | 01.02.2032 09:00 Test. | + | little_endian_dates.yaml | 2020-01-01: Test. | 01.01.2020 09:00 Test. | + | little_endian_dates.yaml | 2020-12-31: Test. | 31.12.2020 09:00 Test. | + + + Scenario Outline: Searching for dates with custom date + Given we use the config "" + When we run "jrnl " + Then the output should be "" + + Examples: Day-first Dates + | config_file | command | expected_output | + | little_endian_dates.yaml | -on '2013-07-10' --short | 10.07.2013 15:40 Life is good. | + | little_endian_dates.yaml | -on 'june 9 2013' --short | 09.06.2013 15:39 My first entry. | + | little_endian_dates.yaml | -on 'july 10 2013' --short | 10.07.2013 15:40 Life is good. | + | little_endian_dates.yaml | -on 'june 2013' --short | 09.06.2013 15:39 My first entry. | + | little_endian_dates.yaml | -on 'july 2013' --short | 10.07.2013 15:40 Life is good. | + # @todo month alone with no year should work + # | little_endian_dates.yaml | -on 'june' --short | 09.06.2013 15:39 My first entry. | + # | little_endian_dates.yaml | -on 'july' --short | 10.07.2013 15:40 Life is good. | + + + Scenario: Writing an entry at the prompt with custom date + Given we use the config "little_endian_dates.yaml" + When we run "jrnl" and enter "2013-05-10: I saw Elvis. He's alive." + Then we should get no error + When we run "jrnl -999" + Then the output should contain "10.05.2013 09:00 I saw Elvis." + And the output should contain "He's alive." + + + Scenario: Viewing today's entries does not print the entire journal + # see: https://github.com/jrnl-org/jrnl/issues/741 + Given we use the config "simple.yaml" + When we run "jrnl -on today" + Then the output should not contain "Life is good" + And the output should not contain "But I'm better." + + Scenario Outline: Create entry using day of the week as entry date one. + Given we use the config "simple.yaml" + And now is "2019-03-12 01:30:32 PM" + When we run "jrnl " + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "" + Then the output should contain the date "" + + Examples: Days of the week + | command | expected_output | date | + | Monday: entry on a monday | entry on a monday | 2019-03-11 09:00 | + | Tuesday: entry on a tuesday | entry on a tuesday | 2019-03-05 09:00 | + | Wednesday: entry on a wednesday | entry on a wednesday | 2019-03-06 09:00 | + | Thursday: entry on a thursday | entry on a thursday | 2019-03-07 09:00 | + | Friday: entry on a friday | entry on a friday | 2019-03-08 09:00 | + | Saturday: entry on a saturday | entry on a saturday | 2019-03-09 09:00 | + | Sunday: entry on a sunday | entry on a sunday | 2019-03-10 09:00 | + | sunday: entry on a sunday | entry on a sunday | 2019-03-10 09:00 | + | sUndAy: entry on a sunday | entry on a sunday | 2019-03-10 09:00 | + + + Scenario Outline: Create entry using day of the week as entry date two. + Given we use the config "simple.yaml" + And now is "2019-03-12 01:30:32 PM" + When we run "jrnl " + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should contain "" + Then the output should contain the date "" + + Examples: Days of the week + | command | expected_output | date | + | Mon: entry on a monday | entry on a monday | 2019-03-11 09:00 | + | Tue: entry on a tuesday | entry on a tuesday | 2019-03-05 09:00 | + | Wed: entry on a wednesday | entry on a wednesday | 2019-03-06 09:00 | + | Thu: entry on a thursday | entry on a thursday | 2019-03-07 09:00 | + | Fri: entry on a friday | entry on a friday | 2019-03-08 09:00 | + | Sat: entry on a saturday | entry on a saturday | 2019-03-09 09:00 | + | Sun: entry on a sunday | entry on a sunday | 2019-03-10 09:00 | + | sun: entry on a sunday | entry on a sunday | 2019-03-10 09:00 | + | sUn: entry on a sunday | entry on a sunday | 2019-03-10 09:00 | + + + Scenario: Journals with unreadable dates should still be loaded + Given we use the config "unreadabledates.yaml" + When we run "jrnl -2" + Then the output should contain "I've lost track of time." + And the output should contain "Time has no meaning." + + + Scenario: Journals with readable dates AND unreadable dates should still contain all data. + Given we use the config "mostlyreadabledates.yaml" + When we run "jrnl --short" + Then the output should be + 2019-07-01 14:23 The third entry + 2019-07-18 14:23 The first entry + 2019-07-19 14:23 The second entry + + + Scenario: Update near-valid dates after journal is edited + Given we use the config "mostlyreadabledates.yaml" + When we run "jrnl 2222-08-19: I have made it exactly one month into the future." + When we run "jrnl -2" + Then the output should contain "2019-07-19 14:23 The second entry" + + + Scenario: Integers in square brackets should not be read as dates + Given we use the config "brackets.yaml" + When we run "jrnl -1" + Then the output should contain "[1] line starting with 1" + + + # broken still + @skip + Scenario: Dayone entries without timezone information are interpreted in current timezone + Given we use the config "dayone.yaml" + When we run "jrnl -until 'feb 2013'" + Then we should get no error + And the output should contain "2013-01-17T18:37Z" in the local time + + + Scenario: Loading entry with ambiguous time stamp in timezone-aware journal (like Dayone) + #https://github.com/jrnl-org/jrnl/issues/153 + Given we use the config "bug153.yaml" + When we run "jrnl -1" + Then we should get no error + And the output should be + 2013-10-27 03:27 Some text. diff --git a/tests/bdd/features/delete.feature b/tests/bdd/features/delete.feature new file mode 100644 index 00000000..f46ef866 --- /dev/null +++ b/tests/bdd/features/delete.feature @@ -0,0 +1,182 @@ +Feature: Delete entries from journal + Scenario Outline: Delete flag allows deletion of single entry + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl -1" + Then the output should contain "2020-09-24 09:14 The third entry finally" + When we run "jrnl --delete" and enter + N + N + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-29 11:11 Entry the first. + 2020-08-31 14:32 A second entry in what I hope to be a long series. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Backing out of interactive delete does not change journal + Given we use the config "" + When we run "jrnl --delete -n 1" and enter + N + When we run "jrnl -99 --short" + Then the output should be + 2020-08-29 11:11 Entry the first. + 2020-08-31 14:32 A second entry in what I hope to be a long series. + 2020-09-24 09:14 The third entry finally after weeks without writing. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + + Scenario Outline: Delete flag with nonsense input deletes nothing (issue #932) + Given we use the config "" + When we run "jrnl --delete asdfasdf" + When we run "jrnl -99 --short" + Then the output should be + 2020-08-29 11:11 Entry the first. + 2020-08-31 14:32 A second entry in what I hope to be a long series. + 2020-09-24 09:14 The third entry finally after weeks without writing. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + + Scenario Outline: Delete flag with tag only deletes tagged entries + Given we use the config "" + When we run "jrnl --delete @ipsum" and enter + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-31 14:32 A second entry in what I hope to be a long series. + 2020-09-24 09:14 The third entry finally after weeks without writing. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Delete flag with multiple tags deletes all entries matching any of the tags + Given we use the config "" + When we run "jrnl --delete @ipsum @tagthree" and enter + Y + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-31 14:32 A second entry in what I hope to be a long series. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Delete flag with -and deletes boolean AND of tagged entries + Given we use the config "" + When we run "jrnl --delete -and @tagone @tagtwo" and enter + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-31 14:32 A second entry in what I hope to be a long series. + 2020-09-24 09:14 The third entry finally after weeks without writing. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Delete flag with -not does not delete entries from given tag + Given we use the config "" + When we run "jrnl --delete @tagone -not @ipsum" and enter + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-29 11:11 Entry the first. + 2020-08-31 14:32 A second entry in what I hope to be a long series. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Delete flag with -from search operator only deletes entries since that date + Given we use the config "" + When we run "jrnl --delete -from 2020-09-01" and enter + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-29 11:11 Entry the first. + 2020-08-31 14:32 A second entry in what I hope to be a long series. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Delete flag with -to only deletes entries up to specified date + Given we use the config "" + When we run "jrnl --delete -to 2020-08-31" and enter + Y + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-09-24 09:14 The third entry finally after weeks without writing. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Delete flag with -starred only deletes starred entries + Given we use the config "" + When we run "jrnl --delete -starred" and enter + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-29 11:11 Entry the first. + 2020-09-24 09:14 The third entry finally after weeks without writing. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + + Scenario Outline: Delete flag with -contains only entries containing expression + Given we use the config "" + When we run "jrnl --delete -contains dignissim" and enter + Y + When we run "jrnl -99 --short" + Then the output should be + 2020-08-31 14:32 A second entry in what I hope to be a long series. + 2020-09-24 09:14 The third entry finally after weeks without writing. + + Examples: Configs + | config_file | + | basic_onefile.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo diff --git a/tests/bdd/features/encrypt.feature b/tests/bdd/features/encrypt.feature new file mode 100644 index 00000000..31d53520 --- /dev/null +++ b/tests/bdd/features/encrypt.feature @@ -0,0 +1,42 @@ +Feature: Encrypting and decrypting journals + + Scenario: Decrypting a journal + Given we use the config "encrypted.yaml" + When we run "jrnl --decrypt" and enter "bad doggie no biscuit" + Then we should see the message "Journal decrypted" + And the config for journal "default" should contain "encrypt: false" + When we run "jrnl -99 --short" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + + + @todo + Scenario: Trying to decrypt an already unencrypted journal + # This should warn the user that the journal is already encrypted + Given we use the config "simple.yaml" + When we run "jrnl --decrypt" + Then the config for journal "default" should contain "encrypt: false" + When we run "jrnl -99 --short" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + + + @todo + Scenario: Trying to encrypt an already encrypted journal + # This should warn the user that the journal is already encrypted + + + Scenario: 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 contain "encrypt: true" + When we run "jrnl -n 1" and enter "swordfish" + Then we should be prompted for a password + And the output should contain "2013-06-10 15:40 Life is good" + diff --git a/tests/bdd/features/file_storage.feature b/tests/bdd/features/file_storage.feature new file mode 100644 index 00000000..f81f2710 --- /dev/null +++ b/tests/bdd/features/file_storage.feature @@ -0,0 +1,52 @@ +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" + And the journal directory should contain + 2013/07/23.txt + + Scenario: Adding multiple entries to a Folder journal should generate multiple date files + Given we use the config "empty_folder.yaml" + When we run "jrnl 23 July 2013: Testing folder journal." + And we run "jrnl 3/7/2014: Second entry of journal." + Then we should see the message "Entry added" + And the journal directory should contain + 2013/07/23.txt + + Scenario: If the journal and its parent directory don't exist, they should be created + Given we use the config "missing_directory.yaml" + Then the journal should not exist + When we run "jrnl This is a new entry in my journal" + Then the journal should exist + When we run "jrnl -99 --short" + Then the output should contain "This is a new entry in my journal" + + 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 -99 --short" + Then the output should contain "This is a new entry in my journal" + + Scenario: Creating journal with relative path should update to absolute path + When we run "jrnl hello world" and enter + test.txt + n + Then the output should contain "Journal 'default' created" + When we change directory to "subfolder" + And we run "jrnl -n 1" + Then the output should contain "hello world" + + Scenario: the temporary filename suffix should default to ".jrnl" + Given we use the config "editor.yaml" + When we run "jrnl --edit" + Then the editor should have been called + Then the editor filename should end with ".jrnl" + + Scenario: the temporary filename suffix should be "-{template_filename}" + Given we use the config "editor_markdown_extension.yaml" + When we run "jrnl --edit" + Then the editor filename should end with "-extension.md" diff --git a/tests/bdd/features/format.feature b/tests/bdd/features/format.feature new file mode 100644 index 00000000..b9e2e384 --- /dev/null +++ b/tests/bdd/features/format.feature @@ -0,0 +1,561 @@ +Feature: Custom formats + + Scenario Outline: JSON format + Given we use the config "" + 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 valid JSON + Given we parse the output as JSON + Then "entries" in the parsed output should have 3 elements + And "tags" in the parsed output should be + @ipsum + @tagone + @tagtwo + @tagthree + And "entries.0.tags" in the parsed output should have 3 elements + And "entries.1.tags" in the parsed output should have 1 elements + And "entries.2.tags" in the parsed output should have 2 elements + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario: Exporting dayone to json should include UUID + Given we use the config "dayone.yaml" + When we run "jrnl --export json" + Then we should get no error + And the output should be valid JSON + Given we parse the output as JSON + Then "entries.0.uuid" in the parsed output should be + 4BB1F46946AD439996C9B59DE7C4DDC1 + + Scenario Outline: Printing a journal that has multiline entries with tags + Given we use the config "" + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Exporting using filters should only export parts of the journal + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl -until 'August 2020' --format json" + Then the output should be valid JSON + Then we should get no error + And the output should be valid JSON + Given we parse the output as JSON + Then "entries" in the parsed output should have 2 elements + And "tags" in the parsed output should be + @ipsum + @tagone + @tagtwo + And "entries.0.tags" in the parsed output should have 3 elements + And "entries.1.tags" in the parsed output should have 1 elements + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Exporting using custom templates + Given we use the config "" + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + @skip_editor # .TODO return after editor steps implemented + Scenario Outline: Increasing Headings on Markdown export + Given we use the config "" + 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 + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + # | basic_dayone.yaml | @todo + + @skip_editor # .TODO return after editor steps implemented + 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 "" + 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. + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + # | basic_dayone.yaml | @todo + + @skip + Scenario Outline: Exporting to XML + Given we use the config "" + 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" 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_file | + # | basic_onefile.yaml | @todo + # | basic_encrypted.yaml | @todo + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @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 valid XML + Given we parse the output as XML + Then "entries" in the parsed output should have 2 elements + And "tags" in the parsed output should be + @idea + @journal + @dan + And there should be 7 "tag" elements + + Scenario Outline: Exporting tags + Given we use the config "" + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + @todo + Scenario Outline: Exporting fancy + # Needs better emoji support + Given we use the config "" + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + @skip_win + Scenario Outline: Export to yaml + Given we use the config "" + And we use the password "test" if prompted + And we create a cache directory + When we run "jrnl --format yaml --file {cache_dir}" + Then the cache directory should contain the files + 2020-08-29_entry-the-first.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 + body: | + Lorem @ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada + quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque + augue et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu + consequat. Aenean ante ex, elementum ut interdum et, mattis eget lacus. In + commodo nulla nec tellus placerat, sed ultricies metus bibendum. Duis eget + venenatis erat. In at dolor dui. @tagone and maybe also @tagtwo. + + Curabitur accumsan nunc ac neque tristique, eleifend faucibus justo + ullamcorper. Suspendisse at mattis nunc. Nullam eget lacinia urna. Suspendisse + potenti. Ut urna est, venenatis sed ante in, ultrices congue mi. Maecenas eget + molestie metus. Mauris porttitor dui ornare gravida porta. Quisque sed lectus + hendrerit, lacinia ante eget, vulputate ante. Aliquam vitae erat non felis + feugiat sagittis. Phasellus quis arcu fringilla, mattis ligula id, vestibulum + urna. Vivamus facilisis leo a mi tincidunt condimentum. Donec eu euismod enim. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu ligula eget + velit scelerisque fringilla. Phasellus pharetra justo et nulla fringilla, ac + porta sapien accumsan. Class aptent taciti sociosqu ad litora torquent per + conubia nostra, per inceptos himenaeos. + ... + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + # | basic_dayone.yaml | + + @skip_win # @todo YAML exporter does not correctly export emoji on Windows + Scenario Outline: Add a blank line to 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 "" + 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 + body: | + I'm so excited about emojis. 💯 🎶 💩 + + Donec semper pellentesque iaculis. Nullam cursus et justo sit amet venenatis. + Vivamus tempus ex dictum metus vehicula gravida. Aliquam sed sem dolor. Nulla + eget ultrices purus. Quisque at nunc at quam pharetra consectetur vitae quis + dolor. Fusce ultricies purus eu est feugiat, quis scelerisque nibh malesuada. + Quisque egestas semper nibh in hendrerit. Nam finibus ex in mi mattis + vulputate. Sed mauris urna, consectetur in justo eu, volutpat accumsan justo. + Phasellus aliquam lacus placerat convallis vestibulum. Curabitur maximus at + ante eget fringilla. @tagthree and also @tagone + ... + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + # | basic_dayone.yaml | @todo + + Scenario: Empty DayOne entry bodies should not error + # https://github.com/jrnl-org/jrnl/issues/780 + Given we use the config "bug780.yaml" + 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 "" + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: -s displays the short version of entries (only the title) + Given we use the config "" + 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_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario: Markdown Support from config file + Given we use the config "format_md.yaml" + When we run "jrnl -n 1" + Then the output should be + # 2013 + + ## June + + ### 2013-06-10 15:40 Life is good. + + But I'm better. + + Scenario: Text Formatter from config file + Given we use the config "format_text.yaml" + When we run "jrnl -n 1" + Then the output should be + [2013-06-10 15:40] Life is good. + But I'm better. + + Scenario Outline: Exporting entries with Cyrillic characters to directory should not fail + Given we use the config "" + And we use the password "test" if prompted + And we create a cache directory + When we run "jrnl 2020-11-21: Первая" + When we run "jrnl --format md --file {cache_dir} -on 2020-11-21" + Then the cache should contain the files + 2020-11-21_первая.md + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Export date counts + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl 2020-08-31 01:01: Hi." + And we run "jrnl --format dates" + Then the output should be + 2020-08-29, 1 + 2020-08-31, 2 + 2020-09-24, 1 + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | diff --git a/tests/bdd/features/import.feature b/tests/bdd/features/import.feature new file mode 100644 index 00000000..d75d6017 --- /dev/null +++ b/tests/bdd/features/import.feature @@ -0,0 +1,88 @@ +Feature: Importing data + + Scenario Outline: --import allows new entry from stdin + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl --import" and pipe "[2020-07-05 15:00] Observe and import." + When we run "jrnl -9 --short" + Then the output should contain "Observe and import" + + Examples: Configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + Scenario Outline: --import allows new large entry from stdin + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl --import" and pipe + [2020-07-05 15:00] Observe and import. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada quis + est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque augue + et venenatis facilisis. Suspendisse potenti. Sed dignissim sed nisl eu consequat. + Aenean ante ex, elementum ut interdum et, mattis eget lacus. In commodo nulla nec + tellus placerat, sed ultricies metus bibendum. Duis eget venenatis erat. In at + dolor dui end of entry. + When we run "jrnl -on 2020-07-05" + Then the output should contain "2020-07-05 15:00 Observe and import." + And the output should contain "Lorem ipsum" + And the output should contain "end of entry." + + Examples: Configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + Scenario Outline: --import allows multiple new entries from stdin + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl --import" and pipe + [2020-07-05 15:00] Observe and import. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + + [2020-07-05 15:01] Twice as nice. + Sed dignissim sed nisl eu consequat. + When we run "jrnl -on 2020-07-05" + Then the output should contain "2020-07-05 15:00 Observe and import." + And the output should contain "Lorem ipsum" + And the output should contain "2020-07-05 15:01 Twice as nice." + And the output should contain "Sed dignissim" + + Examples: Configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + # | basic_folder.yaml | @todo + # | basic_dayone.yaml | @todo + + Scenario: --import allows import new entries from file + Given we use the config "simple.yaml" + When we run "jrnl -99" + Then the output should contain "My first entry." + And the output should contain "Life is good." + But the output should not contain "I have an @idea" + And the output should not contain "I met with" + When we run "jrnl --import --file features/journals/tags.journal" + And we run "jrnl -99" + Then the output should contain "My first entry." + And the output should contain "Life is good." + And the output should contain "PROFIT!" + + Scenario: --import prioritizes --file over pipe data if both are given + Given we use the config "simple.yaml" + When we run "jrnl -99" + Then the output should contain "My first entry." + And the output should contain "Life is good." + But the output should not contain "I have an @idea" + And the output should not contain "I met with" + When we run "jrnl --import --file features/journals/tags.journal" and pipe + [2020-07-05 15:00] I should not exist! + And we run "jrnl -99" + Then the output should contain "My first entry." + And the output should contain "PROFIT!" + But the output should not contain "I should not exist!" + diff --git a/tests/bdd/features/multiple_journals.feature b/tests/bdd/features/multiple_journals.feature new file mode 100644 index 00000000..cd531ee2 --- /dev/null +++ b/tests/bdd/features/multiple_journals.feature @@ -0,0 +1,90 @@ +Feature: Multiple journals + + Scenario: Loading a config with two journals + Given we use the config "multiple.yaml" + When we run "jrnl -99 --short" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + When we run "jrnl work -99 --short" + Then the output should be empty + + Scenario: Write to default config by default + Given we use the config "multiple.yaml" + When we run "jrnl this goes to default" + When we run "jrnl -99 --short" + Then the output should contain + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + Then the output should contain + this goes to default + When we run "jrnl work -99 --short" + Then the output should be empty + + Scenario: Write to specified journal + Given we use the config "multiple.yaml" + When we run "jrnl work a long day in the office" + When we run "jrnl -99 --short" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + When we run "jrnl work -99 --short" + Then the output should contain "a long day in the office" + + Scenario: Tell user which journal was used + Given we use the config "multiple.yaml" + When we run "jrnl work a long day in the office" + Then we should see the message "Entry added to work journal" + + Scenario: Write to specified journal with a timestamp + Given we use the config "multiple.yaml" + When we run "jrnl work 23 july 2012: a long day in the office" + When we run "jrnl -99 --short" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + When we run "jrnl work -99 --short" + Then the output should be + 2012-07-23 09:00 a long day in the office + + Scenario: Write to specified journal without a timestamp but with colon + Given we use the config "multiple.yaml" + When we run "jrnl work : a long day in the office" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + When we run "jrnl work -99 --short" + Then the output should be contain + a long day in the office + + Scenario: Write to specified journal without a timestamp but with colon + Given we use the config "multiple.yaml" + When we run "jrnl work: a long day in the office" + When we run "jrnl -99 --short" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + When we run "jrnl work -99 --short" + Then the output should contain + a long day in the office + + Scenario: Create new journals as required + Given we use the config "multiple.yaml" + Then journal "ideas" should not exist + When we run "jrnl ideas 23 july 2012: sell my junk on ebay and make lots of money" + When we run "jrnl ideas -99 --short" + Then the output should be + 2012-07-23 09:00 sell my junk on ebay and make lots of money + + Scenario: Don't crash if no default journal is specified + Given we use the config "bug343.yaml" + When we run "jrnl a long day in the office" + Then the output should contain "No default journal configured" + + Scenario: Don't crash if no file exists for a configured encrypted journal + Given we use the config "multiple.yaml" + When we run "jrnl new_encrypted Adding first entry" and enter + these three eyes + these three eyes + n + Then the output should contain "Encrypted journal 'new_encrypted' created" diff --git a/tests/bdd/features/password.feature b/tests/bdd/features/password.feature new file mode 100644 index 00000000..ed8aea5f --- /dev/null +++ b/tests/bdd/features/password.feature @@ -0,0 +1,122 @@ +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 contain "encrypt: true" + When we run "jrnl simple -n 1" + Then the output should contain "2013-06-10 15:40 Life is good" + + + Scenario: Encrypt journal with no keyring backend and do not store in keyring + Given we use the config "simple.yaml" + When we run "jrnl test entry" + And we run "jrnl --encrypt" and enter + password + password + n + Then we should get no error + And the output should not contain "Failed to retrieve keyring" + + + Scenario: Encrypt journal with no keyring backend and do store in keyring + Given we use the config "simple.yaml" + When we run "jrnl test entry" + And we run "jrnl --encrypt" and enter + password + password + y + Then we should get no error + And the output should not contain "Failed to retrieve keyring" + # @todo add step to check contents of keyring + + + @todo + Scenario: Open an encrypted journal with wrong password in keyring + # This should ask the user for the password after the keyring fails + + + @todo + Scenario: Decrypt journal with password in keyring + + + @todo + Scenario: Decrypt journal without a keyring + + + Scenario: Encrypt journal when keyring exists but fails + Given we use the config "simple.yaml" + And we have a failed keyring + When we run "jrnl --encrypt" and enter + this password will not be saved in keyring + this password will not be saved in keyring + y + Then we should see the message "Failed to retrieve keyring" + And we should get no error + And we should be prompted for a password + And the config for journal "default" should contain "encrypt: true" + + + Scenario: Decrypt journal when keyring exists but fails + Given we use the config "encrypted.yaml" + And we have a failed keyring + And we use the password "bad doggie no biscuit" if prompted + When we run "jrnl --decrypt" + Then the error output should contain "Failed to retrieve keyring" + And we should get no error + And we should be prompted for a password + And we should see the message "Journal decrypted" + And the config for journal "default" should contain "encrypt: false" + When we run "jrnl --short" + Then we should not be prompted for a password + And the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + + + Scenario: Open encrypted journal when keyring exists but fails + # This should ask the user for the password after the keyring fails + Given we use the config "encrypted.yaml" + And we have a failed keyring + And we use the password "bad doggie no biscuit" if prompted + When we run "jrnl -n 1" + Then we should get no error + And we should be prompted for a password + And the output should contain "Failed to retrieve keyring" + And the output should contain "2013-06-10 15:40 Life is good" + + + Scenario: Mistyping your password + Given we use the config "simple.yaml" + When we run "jrnl --encrypt" and enter + swordfish + sordfish + Then we should be prompted for a password + And we should see the message "Passwords did not match" + And the config for journal "default" should not contain "encrypt: true" + When we run "jrnl --short" + Then the output should be + 2013-06-09 15:39 My first entry. + 2013-06-10 15:40 Life is good. + + + Scenario: Mistyping your password, then getting it right + Given we use the config "simple.yaml" + When we run "jrnl --encrypt" and enter + swordfish + sordfish + swordfish + swordfish + n + Then we should be prompted for a password + And we should see the message "Passwords did not match" + And we should see the message "Journal encrypted" + And the config for journal "default" should contain "encrypt: true" + When we run "jrnl -1" and enter "swordfish" + Then we should be prompted for a password + And the output should contain "2013-06-10 15:40 Life is good" + diff --git a/tests/bdd/features/search.feature b/tests/bdd/features/search.feature new file mode 100644 index 00000000..8d951aaf --- /dev/null +++ b/tests/bdd/features/search.feature @@ -0,0 +1,293 @@ +Feature: Searching in a journal + + Scenario Outline: Displaying entries using -on today should display entries created today + Given we use the config "" + When we run "jrnl today: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl -on today" + Then the output should contain "Adding an entry right now." + But the output should not contain "Everything is alright" + And the output should not contain "Life is good" + + Examples: configs + | config_file | + | simple.yaml | + | empty_folder.yaml | + | dayone.yaml | + + Scenario Outline: Displaying entries using -from day should display correct entries + Given we use the config "" + When we run "jrnl yesterday: This thing happened yesterday" + Then we should see the message "Entry added" + When we run "jrnl today at 11:59pm: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl tomorrow: A future entry." + Then we should see the message "Entry added" + When we run "jrnl -from today" + Then the output should contain "Adding an entry right now." + And the output should contain "A future entry." + And the output should not contain "This thing happened yesterday" + + Examples: configs + | config_file | + | simple.yaml | + | empty_folder.yaml | + | dayone.yaml | + + Scenario Outline: Displaying entries using -from and -to day should display correct entries + Given we use the config "" + When we run "jrnl yesterday: This thing happened yesterday" + Then we should see the message "Entry added" + When we run "jrnl today at 11:59pm: Adding an entry right now." + Then we should see the message "Entry added" + When we run "jrnl tomorrow: A future entry." + Then we should see the message "Entry added" + When we run "jrnl -from yesterday -to today" + Then the output should contain "This thing happened yesterday" + And the output should contain "Adding an entry right now." + And the output should not contain "A future entry." + + Examples: configs + | config_file | + | simple.yaml | + | empty_folder.yaml | + | dayone.yaml | + + Scenario Outline: Searching for a string + Given we use the config "" + When we run "jrnl -contains first --short" + Then we should get no error + And the output should be + 2020-08-29 11:11 Entry the first. + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Searching for a string within tag results + Given we use the config "" + When we run "jrnl @tagone -contains maybe" + Then we should get no error + And the output should contain "maybe" + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Searching for a string within AND tag results + Given we use the config "" + When we run "jrnl -and @tagone @tagtwo -contains maybe" + Then we should get no error + And the output should contain "maybe" + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Searching for a string within NOT tag results + Given we use the config "" + When we run "jrnl -not @tagone -contains lonesome" + Then we should get no error + And the output should contain "lonesome" + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Searching for dates + Given we use the config "" + When we run "jrnl -on 2020-08-31 --short" + Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series." + When we run "jrnl -on 'august 31 2020' --short" + Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series." + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario: Out of order entries to a Folder journal should be listed in date order + Given we use the config "empty_folder.yaml" + When we run "jrnl 3/7/2014 4:37pm: Second entry of journal." + Then we should see the message "Entry added" + When we run "jrnl 23 July 2013: Testing folder journal." + Then we should see the message "Entry added" + When we run "jrnl -2" + Then the output should be + 2013-07-23 09:00 Testing folder journal. + + 2014-03-07 16:37 Second entry of journal. + + Scenario Outline: Searching for all tags should show counts of each tag + Given we use the config "" + When we run "jrnl --tags" + Then we should get no error + And the output should be + @tagtwo : 2 + @tagone : 2 + @tagthree : 1 + @ipsum : 1 + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Filtering journals should also filter tags + Given we use the config "" + When we run "jrnl -from 'september 2020' --tags" + Then we should get no error + And the output should be + @tagthree : 1 + @tagone : 1 + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Excluding a tag should filter out all entries with that tag + Given we use the config "" + When we run "jrnl --tags -not @tagtwo" + Then the output should be + @tagthree : 1 + @tagone : 1 + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Excluding multiple tags should filter out all entries with those tags + Given we use the config "" + When we run "jrnl --tags -not @tagone -not @tagthree" + Then the output should be + @tagtwo : 1 + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario: DayOne tag searching should work with tags containing a mixture of upper and lower case. + # https://github.com/jrnl-org/jrnl/issues/354 + Given we use the config "dayone.yaml" + When we run "jrnl @plAy" + Then the output should contain "2013-05-17 11:39 This entry has tags!" + + Scenario: Loading a sample journal + Given we use the config "simple.yaml" + When we run "jrnl -2" + Then we should get no error + And the output should be + 2013-06-09 15:39 My first entry. + | Everything is alright + + 2013-06-10 15:40 Life is good. + | But I'm better. + + Scenario Outline: Searching by month + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl -month 9 --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + When we run "jrnl -month Sept --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + When we run "jrnl -month September --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Searching by day + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl -day 31 --short" + Then the output should be "2020-08-31 14:32 A second entry in what I hope to be a long series." + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Searching by year + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl 2019-01-01 01:01: I like this year." + And we run "jrnl -year 2019 --short" + Then the output should be "2019-01-01 01:01 I like this year." + When we run "jrnl -year 19 --short" + Then the output should be "2019-01-01 01:01 I like this year." + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Combining month, day, and year search terms + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl -month 08 -day 29 --short" + Then the output should be "2020-08-29 11:11 Entry the first." + When we run "jrnl -day 29 -year 2020 --short" + Then the output should be "2020-08-29 11:11 Entry the first." + When we run "jrnl -month 09 -year 2020 --short" + Then the output should be "2020-09-24 09:14 The third entry finally after weeks without writing." + When we run "jrnl -month 08 -day 29 -year 2020 --short" + Then the output should be "2020-08-29 11:11 Entry the first." + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Searching today in history + Given we use the config "" + And we use the password "test" if prompted + And now is "2020-08-31 02:32:00 PM" + When we run "jrnl 2019-08-31 01:01: Hi, from last year." + And we run "jrnl -today-in-history --short" + Then the output should be + 2019-08-31 01:01 Hi, from last year. + 2020-08-31 14:32 A second entry in what I hope to be a long series. + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario: Loading a DayOne Journal + Given we use the config "dayone.yaml" + When we run "jrnl -from 'feb 2013'" + Then we should get no error + And the output should be + 2013-05-17 11:39 This entry has tags! + + 2013-06-17 20:38 This entry has a location. + + 2013-07-17 11:38 This entry is starred! diff --git a/tests/bdd/features/star.feature b/tests/bdd/features/star.feature new file mode 100644 index 00000000..7b1b42f1 --- /dev/null +++ b/tests/bdd/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 "" + 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.yaml | + | empty_folder.yaml | + | dayone.yaml | + + Scenario Outline: Filtering by starred entries will show only starred entries + Given we use the config "" + 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.yaml | + | empty_folder.yaml | + | dayone_empty.yaml | + + Scenario: Starring an entry will mark it in an encrypted journal + Given we use the config "encrypted.yaml" + 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/tests/bdd/features/tag.feature b/tests/bdd/features/tag.feature new file mode 100644 index 00000000..a62b5ac8 --- /dev/null +++ b/tests/bdd/features/tag.feature @@ -0,0 +1,48 @@ +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 "" + 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_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Emails addresses should not be parsed as tags + Given we use the config "" + 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" + 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_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Entry can start and end with tags + Given we use the config "" + 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_file | + | basic_onefile.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | diff --git a/tests/bdd/features/upgrade.feature b/tests/bdd/features/upgrade.feature new file mode 100644 index 00000000..e5714a82 --- /dev/null +++ b/tests/bdd/features/upgrade.feature @@ -0,0 +1,54 @@ +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" + When we run "jrnl -99 --short" + Then the output should be + 2010-06-10 15:00 A life without chocolate is like a bad analogy. + 2013-06-10 15:40 He said "[this] is the best time to be alive".Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada + And the output should contain + 2010-06-10 15:00 A life without chocolate is like a bad analogy. + And the output should contain + 2013-06-10 15:40 He said "[this] is the best time to be alive". + + Scenario: Upgrading a journal encrypted with jrnl 1.x + Given we use the config "encrypted_old.json" + When we run "jrnl -n 1" and enter + Y + bad doggie no biscuit + bad doggie no biscuit + Then we should be prompted for a password + And the output should contain "2013-06-10 15:40 Life is good" + + Scenario: Upgrading a config without colors to colors + Given we use the config "no_colors.yaml" + When we run "jrnl -n 1" + Then the config should contain + colors: + date: none + title: none + body: none + tags: none + + Scenario: Upgrade and parse journals with little endian date format + Given we use the config "upgrade_from_195_little_endian_dates.json" + When we run "jrnl -9 --short" 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". + + Scenario: Upgrade with missing journal + Given we use the config "upgrade_from_195_with_missing_journal.json" + When we run "jrnl --list" and enter "Y" + Then the output should contain "Error: features/journals/missing.journal does not exist." + And we should get no error + + Scenario: Upgrade with missing encrypted journal + Given we use the config "upgrade_from_195_with_missing_encrypted_journal.json" + When we run "jrnl --list" and enter + Y + bad doggie no biscuit + Then the output should contain "Error: features/journals/missing.journal does not exist." + And the output should contain "We're all done" + And we should get no error diff --git a/tests/bdd/features/write.feature b/tests/bdd/features/write.feature new file mode 100644 index 00000000..1cb822b5 --- /dev/null +++ b/tests/bdd/features/write.feature @@ -0,0 +1,213 @@ +Feature: Writing new entries. + + Scenario Outline: Multiline entry with punctuation should keep title punctuation + Given we use the config "" + 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.yaml | + | empty_folder.yaml | + | dayone.yaml | + | encrypted.yaml | + + Scenario Outline: Single line entry with period should be split at period + Given we use the config "" + 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.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: CJK entry should be split at fullwidth period without following space. + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl 七転び。八起き" + And we run "jrnl -1" + Then the output should contain "| 八起き" + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_folder.yaml | + | basic_dayone.yaml | + + Scenario Outline: Writing an entry from command line should store the entry + Given we use the config "" + 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.yaml | + | empty_folder.yaml | + | dayone.yaml | + | encrypted.yaml | + + Scenario Outline: Writing a partial entry from command line with edit flag should go to the editor + Given we use the config "" + And we use the password "test" if prompted + When we run "jrnl this is a partial --edit" + Then we should see the message "Entry added" + Then the editor should have been called + And the editor file content should be + this is a partial + + Examples: configs + | config_file | + | basic_onefile.yaml | + | basic_encrypted.yaml | + | basic_dayone.yaml | + | basic_folder.yaml | + + Scenario Outline: Writing an empty entry from the editor should yield "Nothing saved to file" message + Given we use the config "" + And we write nothing to the editor if opened + And we use the password "test" if prompted + When we run "jrnl --edit" + Then the error output should contain "[Nothing saved to file]" + And the editor should have been called + + Examples: configs + | config_file | + | editor.yaml | + | editor_empty_folder.yaml | + | dayone.yaml | + | basic_encrypted.yaml | + | basic_onefile.yaml | + + @skip + Scenario Outline: Writing an empty entry from the command line with no editor should yield nothing + Given we use the config "" + 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 | + | config_simple.yaml | + | empty_folder.yaml | + | encrypted.yaml | + # | dayone | @todo + + Scenario Outline: Writing an entry does not print the entire journal + # https://github.com/jrnl-org/jrnl/issues/87 + Given we use the config "" + 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.yaml | + | editor_empty_folder.yaml | + | dayone.yaml | + | encrypted.yaml | + + Scenario Outline: Embedded period stays in title + Given we use the config "" + 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.yaml | + | empty_folder.yaml | + | dayone.yaml | + | encrypted.yaml | + + Scenario Outline: Write and read emoji support + Given we use the config "" + 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.yaml | + | empty_folder.yaml | + | dayone.yaml | + | encrypted.yaml | + + Scenario Outline: Writing an entry at the prompt (no editor) should store the entry + Given we use the config "" + 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.yaml | + | empty_folder.yaml | + | encrypted.yaml | + + @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 we should get no error + And the output should be valid JSON + Given we parse the output as JSON + Then "entries" in the parsed output should have 5 elements + And "entries.0.creator" in the parsed output should be + software_agent + os_agent + host_name + generation_date + device_agent + And "entries.0.creator.software_agent" in the parsed output should contain + jrnl + + 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 am feeling sore because I forgot to stretch." + Then we should see the message "Entry added" + When we run "jrnl -1" + Then the output should be + 2014-04-24 09:00 Ran 6.2 miles today in 1:02:03. + | I am feeling sore because I forgot to stretch. + + Scenario: Opening an folder that's not a DayOne folder should treat as folder journal + Given we use the config "empty_folder.yaml" + 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." diff --git a/tests/bdd/test_features.py b/tests/bdd/test_features.py new file mode 100644 index 00000000..b824df39 --- /dev/null +++ b/tests/bdd/test_features.py @@ -0,0 +1,17 @@ +from pytest_bdd import scenarios + +scenarios("features/build.feature") +scenarios("features/core.feature") +scenarios("features/datetime.feature") +scenarios("features/delete.feature") +scenarios("features/encrypt.feature") +scenarios("features/file_storage.feature") +scenarios("features/format.feature") +scenarios("features/import.feature") +scenarios("features/multiple_journals.feature") +scenarios("features/password.feature") +scenarios("features/search.feature") +scenarios("features/star.feature") +scenarios("features/tag.feature") +scenarios("features/upgrade.feature") +scenarios("features/write.feature") diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..277e2602 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,29 @@ +# Copyright (C) 2012-2021 jrnl contributors +# License: https://www.gnu.org/licenses/gpl-3.0.html + +from pytest import mark + +from jrnl.os_compat import on_windows + + +pytest_plugins = [ + "tests.lib.fixtures", + "tests.lib.given_steps", + "tests.lib.when_steps", + "tests.lib.then_steps", +] + + +def pytest_bdd_apply_tag(tag, function): + if tag == "skip_win": + marker = mark.skipif(on_windows(), reason="Skip test on Windows") + elif tag == "skip_editor": + marker = mark.skip( + reason="Skipping editor-related test. We should come back to this!" + ) + else: + # Fall back to pytest-bdd's default behavior + return None + + marker(function) + return True diff --git a/tests/data/configs/basic_dayone.yaml b/tests/data/configs/basic_dayone.yaml new file mode 100644 index 00000000..0209f2f7 --- /dev/null +++ b/tests/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/tests/data/configs/basic_encrypted.yaml b/tests/data/configs/basic_encrypted.yaml new file mode 100644 index 00000000..77f4e48d --- /dev/null +++ b/tests/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/tests/data/configs/basic_folder.yaml b/tests/data/configs/basic_folder.yaml new file mode 100644 index 00000000..ba0de638 --- /dev/null +++ b/tests/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/tests/data/configs/basic_onefile.yaml b/tests/data/configs/basic_onefile.yaml new file mode 100644 index 00000000..fb48c6f8 --- /dev/null +++ b/tests/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/tests/data/configs/brackets.yaml b/tests/data/configs/brackets.yaml new file mode 100644 index 00000000..e658947c --- /dev/null +++ b/tests/data/configs/brackets.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/brackets.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/tests/data/configs/bug153.yaml b/tests/data/configs/bug153.yaml new file mode 100644 index 00000000..ff645ab6 --- /dev/null +++ b/tests/data/configs/bug153.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +journals: + default: features/journals/bug153.dayone +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/tests/data/configs/bug343.yaml b/tests/data/configs/bug343.yaml new file mode 100644 index 00000000..a4e25d8a --- /dev/null +++ b/tests/data/configs/bug343.yaml @@ -0,0 +1,13 @@ +default_hour: 9 +default_minute: 0 +editor: '' +template: false +encrypt: false +highlight: true +journals: + simple: features/journals/simple.journal + work: features/journals/work.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" diff --git a/tests/data/configs/bug780.yaml b/tests/data/configs/bug780.yaml new file mode 100644 index 00000000..e1d830c2 --- /dev/null +++ b/tests/data/configs/bug780.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +journals: + default: features/journals/bug780.dayone +linewrap: 80 +tagsymbols: '@' +template: false +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" diff --git a/tests/data/configs/dayone.yaml b/tests/data/configs/dayone.yaml new file mode 100644 index 00000000..894cb911 --- /dev/null +++ b/tests/data/configs/dayone.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: noop +template: false +encrypt: false +highlight: true +journals: + default: features/journals/dayone.dayone +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" diff --git a/tests/data/configs/dayone_empty.yaml b/tests/data/configs/dayone_empty.yaml new file mode 100644 index 00000000..7750d389 --- /dev/null +++ b/tests/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/tests/data/configs/deletion.yaml b/tests/data/configs/deletion.yaml new file mode 100644 index 00000000..d4155260 --- /dev/null +++ b/tests/data/configs/deletion.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/deletion.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/tests/data/configs/deletion_filters.yaml b/tests/data/configs/deletion_filters.yaml new file mode 100644 index 00000000..73a88e4d --- /dev/null +++ b/tests/data/configs/deletion_filters.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/deletion_filters.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/tests/data/configs/editor-args.yaml b/tests/data/configs/editor-args.yaml new file mode 100644 index 00000000..12c5bd9c --- /dev/null +++ b/tests/data/configs/editor-args.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: vim -f -c 'setf markdown' +encrypt: false +highlight: true +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/tests/data/configs/editor.yaml b/tests/data/configs/editor.yaml new file mode 100644 index 00000000..8a06f916 --- /dev/null +++ b/tests/data/configs/editor.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "vim" +encrypt: false +highlight: true +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/tests/data/configs/editor_empty_folder.yaml b/tests/data/configs/editor_empty_folder.yaml new file mode 100644 index 00000000..1724bbfb --- /dev/null +++ b/tests/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/tests/data/configs/editor_encrypted.yaml b/tests/data/configs/editor_encrypted.yaml new file mode 100644 index 00000000..75273c96 --- /dev/null +++ b/tests/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/tests/data/configs/editor_markdown_extension.yaml b/tests/data/configs/editor_markdown_extension.yaml new file mode 100644 index 00000000..bf3b8d8e --- /dev/null +++ b/tests/data/configs/editor_markdown_extension.yaml @@ -0,0 +1,18 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +editor: "vim" +journals: + default: features/journals/editor_markdown_extension.journal +linewrap: 80 +tagsymbols: "@" +template: features/templates/extension.md +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/tests/data/configs/empty_folder.yaml b/tests/data/configs/empty_folder.yaml new file mode 100644 index 00000000..52a21854 --- /dev/null +++ b/tests/data/configs/empty_folder.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: '' +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/tests/data/configs/encrypted.yaml b/tests/data/configs/encrypted.yaml new file mode 100644 index 00000000..4d50b607 --- /dev/null +++ b/tests/data/configs/encrypted.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: '' +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/tests/data/configs/encrypted_old.json b/tests/data/configs/encrypted_old.json new file mode 100644 index 00000000..e69d9b79 --- /dev/null +++ b/tests/data/configs/encrypted_old.json @@ -0,0 +1,13 @@ +{ + "default_hour": 9, + "default_minute": 0, + "editor": "", + "encrypt": true, + "highlight": true, + "journals": { + "default": "features/journals/encrypted_jrnl-1-9-5.journal" + }, + "linewrap": 80, + "tagsymbols": "@", + "timeformat": "%Y-%m-%d %H:%M" +} diff --git a/tests/data/configs/encrypted_old.yaml b/tests/data/configs/encrypted_old.yaml new file mode 100644 index 00000000..bc7b1440 --- /dev/null +++ b/tests/data/configs/encrypted_old.yaml @@ -0,0 +1,11 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: true +highlight: true +journals: + default: features/journals/encrypted_jrnl1-9-5.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" diff --git a/tests/data/configs/format_md.yaml b/tests/data/configs/format_md.yaml new file mode 100644 index 00000000..0b9f1c3b --- /dev/null +++ b/tests/data/configs/format_md.yaml @@ -0,0 +1,19 @@ +colors: + body: none + date: none + tags: none + title: none +default_hour: 9 +default_minute: 0 +display_format: markdown +editor: '' +encrypt: false +highlight: true +indent_character: '|' +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: '@' +template: false +timeformat: '%Y-%m-%d %H:%M' +version: v2.4.5 diff --git a/tests/data/configs/format_text.yaml b/tests/data/configs/format_text.yaml new file mode 100644 index 00000000..c82ff7a7 --- /dev/null +++ b/tests/data/configs/format_text.yaml @@ -0,0 +1,19 @@ +colors: + body: none + date: none + tags: none + title: none +default_hour: 9 +default_minute: 0 +display_format: text +editor: '' +encrypt: false +highlight: true +indent_character: '|' +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: '@' +template: false +timeformat: '%Y-%m-%d %H:%M' +version: v2.4.5 diff --git a/tests/data/configs/invalid_color.yaml b/tests/data/configs/invalid_color.yaml new file mode 100644 index 00000000..25c0e58d --- /dev/null +++ b/tests/data/configs/invalid_color.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" +colors: + date: not-a-color + title: also-not-a-color + body: still-no-color + tags: me-too diff --git a/tests/data/configs/little_endian_dates.yaml b/tests/data/configs/little_endian_dates.yaml new file mode 100644 index 00000000..223c820d --- /dev/null +++ b/tests/data/configs/little_endian_dates.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/little_endian_dates.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%d.%m.%Y %H:%M" +indent_character: "|" diff --git a/tests/data/configs/markdown-headings-335.yaml b/tests/data/configs/markdown-headings-335.yaml new file mode 100644 index 00000000..4368f641 --- /dev/null +++ b/tests/data/configs/markdown-headings-335.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +template: false +journals: + default: features/journals/markdown-headings-335.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/tests/data/configs/missing_directory.yaml b/tests/data/configs/missing_directory.yaml new file mode 100644 index 00000000..d600404c --- /dev/null +++ b/tests/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/tests/data/configs/missing_journal.yaml b/tests/data/configs/missing_journal.yaml new file mode 100644 index 00000000..a1f6f8cf --- /dev/null +++ b/tests/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/tests/data/configs/mostlyreadabledates.yaml b/tests/data/configs/mostlyreadabledates.yaml new file mode 100644 index 00000000..5e3e1a15 --- /dev/null +++ b/tests/data/configs/mostlyreadabledates.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/mostlyreadabledates.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/tests/data/configs/multiline-tags.yaml b/tests/data/configs/multiline-tags.yaml new file mode 100644 index 00000000..033aaa27 --- /dev/null +++ b/tests/data/configs/multiline-tags.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/multiline-tags.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/tests/data/configs/multiline.yaml b/tests/data/configs/multiline.yaml new file mode 100644 index 00000000..aa35b3f5 --- /dev/null +++ b/tests/data/configs/multiline.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/multiline.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/tests/data/configs/multiple.yaml b/tests/data/configs/multiple.yaml new file mode 100644 index 00000000..1501b383 --- /dev/null +++ b/tests/data/configs/multiple.yaml @@ -0,0 +1,18 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +template: false +journals: + default: features/journals/simple.journal + ideas: features/journals/does-not-exist.journal + simple: features/journals/simple.journal + work: features/journals/work.journal + new_encrypted: + encrypt: true + journal: features/journals/new_encrypted.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" diff --git a/tests/data/configs/no_colors.yaml b/tests/data/configs/no_colors.yaml new file mode 100644 index 00000000..9111b561 --- /dev/null +++ b/tests/data/configs/no_colors.yaml @@ -0,0 +1,12 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/simple.journal +linewrap: 80 +tagsymbols: "@" +template: false +timeformat: "%Y-%m-%d %H:%M" +indent_character: "|" diff --git a/tests/data/configs/simple.yaml b/tests/data/configs/simple.yaml new file mode 100644 index 00000000..020bab18 --- /dev/null +++ b/tests/data/configs/simple.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/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/tests/data/configs/tags-216.yaml b/tests/data/configs/tags-216.yaml new file mode 100644 index 00000000..81b3865f --- /dev/null +++ b/tests/data/configs/tags-216.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +template: false +journals: + default: features/journals/tags-216.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/tests/data/configs/tags-237.yaml b/tests/data/configs/tags-237.yaml new file mode 100644 index 00000000..5aecd61e --- /dev/null +++ b/tests/data/configs/tags-237.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +template: false +journals: + default: features/journals/tags-237.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/tests/data/configs/tags.yaml b/tests/data/configs/tags.yaml new file mode 100644 index 00000000..4b55952c --- /dev/null +++ b/tests/data/configs/tags.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: '' +encrypt: false +highlight: true +template: false +journals: + default: features/journals/tags.journal +linewrap: 80 +tagsymbols: '@' +timeformat: '%Y-%m-%d %H:%M' +indent_character: "|" +colors: + date: none + title: none + body: none + tags: none diff --git a/tests/data/configs/unreadabledates.yaml b/tests/data/configs/unreadabledates.yaml new file mode 100644 index 00000000..99304e5a --- /dev/null +++ b/tests/data/configs/unreadabledates.yaml @@ -0,0 +1,17 @@ +default_hour: 9 +default_minute: 0 +editor: "" +encrypt: false +highlight: true +journals: + default: features/journals/unreadabledates.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/tests/data/configs/upgrade_from_195.json b/tests/data/configs/upgrade_from_195.json new file mode 100644 index 00000000..ec380372 --- /dev/null +++ b/tests/data/configs/upgrade_from_195.json @@ -0,0 +1,11 @@ +{ +"default_hour": 9, +"timeformat": "%Y-%m-%d %H:%M", +"linewrap": 80, +"encrypt": false, +"editor": "", +"default_minute": 0, +"highlight": true, +"journals": {"default": "features/journals/simple_jrnl-1-9-5.journal"}, +"tagsymbols": "@" +} diff --git a/tests/data/configs/upgrade_from_195_little_endian_dates.json b/tests/data/configs/upgrade_from_195_little_endian_dates.json new file mode 100644 index 00000000..7d3c470c --- /dev/null +++ b/tests/data/configs/upgrade_from_195_little_endian_dates.json @@ -0,0 +1,11 @@ +{ +"default_hour": 9, +"timeformat": "%d.%m.%Y %H:%M", +"linewrap": 80, +"encrypt": false, +"editor": "", +"default_minute": 0, +"highlight": true, +"journals": {"default": "features/journals/simple_jrnl-1-9-5_little_endian_dates.journal"}, +"tagsymbols": "@" +} diff --git a/tests/data/configs/upgrade_from_195_with_missing_encrypted_journal.json b/tests/data/configs/upgrade_from_195_with_missing_encrypted_journal.json new file mode 100644 index 00000000..5bbfb5b1 --- /dev/null +++ b/tests/data/configs/upgrade_from_195_with_missing_encrypted_journal.json @@ -0,0 +1,11 @@ +{ +"default_hour": 9, +"timeformat": "%Y-%m-%d %H:%M", +"linewrap": 80, +"encrypt": true, +"editor": "", +"default_minute": 0, +"highlight": true, +"journals": {"default": "features/journals/encrypted_jrnl-1-9-5.journal", "missing": "features/journals/missing.journal"}, +"tagsymbols": "@" +} diff --git a/tests/data/configs/upgrade_from_195_with_missing_journal.json b/tests/data/configs/upgrade_from_195_with_missing_journal.json new file mode 100644 index 00000000..8d456159 --- /dev/null +++ b/tests/data/configs/upgrade_from_195_with_missing_journal.json @@ -0,0 +1,11 @@ +{ +"default_hour": 9, +"timeformat": "%Y-%m-%d %H:%M", +"linewrap": 80, +"encrypt": false, +"editor": "", +"default_minute": 0, +"highlight": true, +"journals": {"default": "features/journals/simple_jrnl-1-9-5.journal", "missing": "features/journals/missing.journal"}, +"tagsymbols": "@" +} diff --git a/tests/data/journals/basic_dayone.dayone/entries/D04D335AFED711EABA18FAFFC2100C3D.doentry b/tests/data/journals/basic_dayone.dayone/entries/D04D335AFED711EABA18FAFFC2100C3D.doentry new file mode 100644 index 00000000..9721dd55 --- /dev/null +++ b/tests/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/tests/data/journals/basic_dayone.dayone/entries/FC8A86CAFED711EA8892FAFFC2100C3D.doentry b/tests/data/journals/basic_dayone.dayone/entries/FC8A86CAFED711EA8892FAFFC2100C3D.doentry new file mode 100644 index 00000000..8c2f3c52 --- /dev/null +++ b/tests/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/tests/data/journals/basic_dayone.dayone/entries/FD8ABC8EFED711EABC35FAFFC2100C3D.doentry b/tests/data/journals/basic_dayone.dayone/entries/FD8ABC8EFED711EABC35FAFFC2100C3D.doentry new file mode 100644 index 00000000..d998c36b --- /dev/null +++ b/tests/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/tests/data/journals/basic_encrypted.journal b/tests/data/journals/basic_encrypted.journal new file mode 100644 index 00000000..ffc122df --- /dev/null +++ b/tests/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/tests/data/journals/basic_folder/2020/08/29.txt b/tests/data/journals/basic_folder/2020/08/29.txt new file mode 100644 index 00000000..c8af54ca --- /dev/null +++ b/tests/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/tests/data/journals/basic_folder/2020/08/31.txt b/tests/data/journals/basic_folder/2020/08/31.txt new file mode 100644 index 00000000..826e7cdb --- /dev/null +++ b/tests/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/tests/data/journals/basic_folder/2020/09/24.txt b/tests/data/journals/basic_folder/2020/09/24.txt new file mode 100644 index 00000000..2bd885ce --- /dev/null +++ b/tests/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/tests/data/journals/basic_onefile.journal b/tests/data/journals/basic_onefile.journal new file mode 100644 index 00000000..0d988049 --- /dev/null +++ b/tests/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/tests/data/journals/brackets.journal b/tests/data/journals/brackets.journal new file mode 100644 index 00000000..4649ea3e --- /dev/null +++ b/tests/data/journals/brackets.journal @@ -0,0 +1,2 @@ +[2019-07-08 05:42] Entry subject +[1] line starting with 1 diff --git a/tests/data/journals/bug153.dayone/entries/B40EE704E15846DE8D45C44118A4D511.doentry b/tests/data/journals/bug153.dayone/entries/B40EE704E15846DE8D45C44118A4D511.doentry new file mode 100644 index 00000000..066821bb --- /dev/null +++ b/tests/data/journals/bug153.dayone/entries/B40EE704E15846DE8D45C44118A4D511.doentry @@ -0,0 +1,56 @@ + + + + + Creation Date + 2013-10-27T02:27:27Z + Creator + + Device Agent + iPhone/iPhone3,1 + Generation Date + 2013-10-27T07:02:27Z + Host Name + omrt104001 + OS Agent + iOS/7.0.3 + Software Agent + Day One (iOS)/1.11.4 + + Entry Text + Some text. + Location + + Administrative Area + Östergötlands län + Country + Sverige + Latitude + 58.383400000000000 + Locality + City + Longitude + 15.577170000000000 + Place Name + Street + + Starred + + Time Zone + Europe/Stockholm + UUID + B40EE704E15846DE8D45C44118A4D511 + Weather + + Celsius + 12 + Description + Clear + Fahrenheit + 54 + IconName + sunnyn.png + + + diff --git a/tests/data/journals/bug153.dayone/entries/B40EE704E15846DE8D45C44118A4D512.doentry b/tests/data/journals/bug153.dayone/entries/B40EE704E15846DE8D45C44118A4D512.doentry new file mode 100644 index 00000000..ea3efec5 --- /dev/null +++ b/tests/data/journals/bug153.dayone/entries/B40EE704E15846DE8D45C44118A4D512.doentry @@ -0,0 +1,52 @@ + + Creation Date + 2013-10-27T02:27:27Z + Creator + + Device Agent + iPhone/iPhone3,1 + Generation Date + 2013-10-27T07:02:27Z + Host Name + omrt104001 + OS Agent + iOS/7.0.3 + Software Agent + Day One (iOS)/1.11.4 + + Entry Text + This is not a valid plist. + Location + + Administrative Area + Östergötlands län + Country + Sverige + Latitude + 58.383400000000000 + Locality + City + Longitude + 15.577170000000000 + Place Name + Street + + Starred + + Time Zone + Europe/Stockholm + UUID + B40EE704E15846DE8D45C44118A4D511 + Weather + + Celsius + 12 + Description + Clear + Fahrenheit + 54 + IconName + sunnyn.png + + + diff --git a/tests/data/journals/bug780.dayone/entries/48A25033B34047C591160A4480197D8B.doentry b/tests/data/journals/bug780.dayone/entries/48A25033B34047C591160A4480197D8B.doentry new file mode 100644 index 00000000..426f1ea8 --- /dev/null +++ b/tests/data/journals/bug780.dayone/entries/48A25033B34047C591160A4480197D8B.doentry @@ -0,0 +1,33 @@ + + + + + Activity + Stationary + Creation Date + 2019-12-30T21:28:54Z + Entry Text + + Starred + + UUID + 48A25033B34047C591160A4480197D8B + Creator + + Device Agent + PC + Generation Date + 2019-12-30T21:28:54Z + Host Name + LE-TREPORT + OS Agent + Microsoft Windows/10 Home + Software Agent + Journaley/2.1 + + Tags + + i_have_no_body + + + diff --git a/tests/data/journals/dayone.dayone/entries/044F3747A38546168B572C2E3F217FA2.doentry b/tests/data/journals/dayone.dayone/entries/044F3747A38546168B572C2E3F217FA2.doentry new file mode 100644 index 00000000..1ac26242 --- /dev/null +++ b/tests/data/journals/dayone.dayone/entries/044F3747A38546168B572C2E3F217FA2.doentry @@ -0,0 +1,34 @@ + + + + + Creation Date + 2013-05-17T18:39:20Z + Creator + + Device Agent + Macintosh/MacBookAir5,2 + Generation Date + 2013-08-17T18:39:20Z + Host Name + Egeria + OS Agent + Mac OS X/10.8.4 + Software Agent + Day One (Mac)/1.8 + + Entry Text + This entry has tags! + Starred + + Tags + + work + PLaY + + Time Zone + America/Los_Angeles + UUID + 044F3747A38546168B572C2E3F217FA2 + + diff --git a/tests/data/journals/dayone.dayone/entries/0BDDD6CDA43C4A9AA2681517CC35AD9D.doentry b/tests/data/journals/dayone.dayone/entries/0BDDD6CDA43C4A9AA2681517CC35AD9D.doentry new file mode 100644 index 00000000..927de884 --- /dev/null +++ b/tests/data/journals/dayone.dayone/entries/0BDDD6CDA43C4A9AA2681517CC35AD9D.doentry @@ -0,0 +1,46 @@ + + + + + Creation Date + 2013-06-17T18:38:29Z + Creator + + Device Agent + Macintosh/MacBookAir5,2 + Generation Date + 2013-08-17T18:38:29Z + Host Name + Egeria + OS Agent + Mac OS X/10.8.4 + Software Agent + Day One (Mac)/1.8 + + Entry Text + This entry has a location. + Location + + Administrative Area + California + Country + Germany + Latitude + 52.4979764 + Locality + Berlin + Longitude + 13.2404758 + Place Name + Abandoned Spy Tower + + Starred + + Tags + + Time Zone + Europe/Berlin + UUID + 0BDDD6CDA43C4A9AA2681517CC35AD9D + + diff --git a/tests/data/journals/dayone.dayone/entries/422BC895507944A291E6FC44FC6B8BFC.doentry b/tests/data/journals/dayone.dayone/entries/422BC895507944A291E6FC44FC6B8BFC.doentry new file mode 100644 index 00000000..16260763 --- /dev/null +++ b/tests/data/journals/dayone.dayone/entries/422BC895507944A291E6FC44FC6B8BFC.doentry @@ -0,0 +1,31 @@ + + + + + Creation Date + 2013-07-17T18:38:08Z + Creator + + Device Agent + Macintosh/MacBookAir5,2 + Generation Date + 2013-08-17T18:38:08Z + Host Name + Egeria + OS Agent + Mac OS X/10.8.4 + Software Agent + Day One (Mac)/1.8 + + Entry Text + This entry is starred! + Starred + + Tags + + Time Zone + America/Los_Angeles + UUID + 422BC895507944A291E6FC44FC6B8BFC + + diff --git a/tests/data/journals/dayone.dayone/entries/4BB1F46946AD439996C9B59DE7C4DDC1.doentry b/tests/data/journals/dayone.dayone/entries/4BB1F46946AD439996C9B59DE7C4DDC1.doentry new file mode 100644 index 00000000..9ebaf538 --- /dev/null +++ b/tests/data/journals/dayone.dayone/entries/4BB1F46946AD439996C9B59DE7C4DDC1.doentry @@ -0,0 +1,29 @@ + + + + + Creation Date + 2013-01-17T18:37:50Z + Creator + + Device Agent + Macintosh/MacBookAir5,2 + Generation Date + 2013-08-17T18:37:50Z + Host Name + Egeria + OS Agent + Mac OS X/10.8.4 + Software Agent + Day One (Mac)/1.8 + + Entry Text + This is a DayOne entry without Timezone. + Starred + + Tags + + UUID + 4BB1F46946AD439996C9B59DE7C4DDC1 + + diff --git a/tests/data/journals/dayone_empty.dayone/entries/empty.txt b/tests/data/journals/dayone_empty.dayone/entries/empty.txt new file mode 100644 index 00000000..c86b8f66 --- /dev/null +++ b/tests/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/tests/data/journals/deletion.journal b/tests/data/journals/deletion.journal new file mode 100644 index 00000000..c0fa689d --- /dev/null +++ b/tests/data/journals/deletion.journal @@ -0,0 +1,5 @@ +[2019-10-29 11:11] First entry. + +[2019-10-29 11:11] Second entry. + +[2019-10-29 11:13] Third entry. \ No newline at end of file diff --git a/tests/data/journals/deletion_filters.journal b/tests/data/journals/deletion_filters.journal new file mode 100644 index 00000000..9a3747db --- /dev/null +++ b/tests/data/journals/deletion_filters.journal @@ -0,0 +1,14 @@ +[2019-10-01 08:00] It's just another day in October. +Not much to write about. + +[2020-01-01 08:00] Happy New Year! +So this is the New Year. @holidays + +[2020-03-01 08:00] It's just another day in March. +A stick, a stone, it's the end of the road. + +[2020-05-01 09:00] Happy May Day! +@holidays @springtime Several holidays fall on this date. + +[2020-05-02 12:10] Writing tests. * +@springtime They will help prevent bugs. diff --git a/tests/data/journals/empty_folder/empty b/tests/data/journals/empty_folder/empty new file mode 100644 index 00000000..175b82b5 --- /dev/null +++ b/tests/data/journals/empty_folder/empty @@ -0,0 +1 @@ +Nothing to see here diff --git a/tests/data/journals/encrypted.journal b/tests/data/journals/encrypted.journal new file mode 100644 index 00000000..d2a5fcbe --- /dev/null +++ b/tests/data/journals/encrypted.journal @@ -0,0 +1 @@ +gAAAAABVIHB7tnwKExG7aC5ZbAbBL9SG2oY2GENeoOJ22i1PZigOvCYvrQN3kpsu0KGr7ay5K-_46R5YFlqJvtQ8anPH2FSITsaZy-l5Lz_5quw3rmzhLwAR1tc0icgtR4MEpXEdsuQ7cyb12Xq-JLDrnATs0id5Vow9Ri_tE7Xe4BXgXaySn3aRPwWKoninVxVPVvETY3MXHSUEXV9OZ-pH5kYBLGYbLA== diff --git a/tests/data/journals/encrypted_jrnl-1-9-5.journal b/tests/data/journals/encrypted_jrnl-1-9-5.journal new file mode 100644 index 00000000..339b47ba Binary files /dev/null and b/tests/data/journals/encrypted_jrnl-1-9-5.journal differ diff --git a/tests/data/journals/little_endian_dates.journal b/tests/data/journals/little_endian_dates.journal new file mode 100644 index 00000000..d7492969 --- /dev/null +++ b/tests/data/journals/little_endian_dates.journal @@ -0,0 +1,5 @@ +[09.06.2013 15:39] My first entry. +Everything is alright + +[10.07.2013 15:40] Life is good. +But I'm better. diff --git a/tests/data/journals/markdown-headings-335.journal b/tests/data/journals/markdown-headings-335.journal new file mode 100644 index 00000000..30f592ef --- /dev/null +++ b/tests/data/journals/markdown-headings-335.journal @@ -0,0 +1,42 @@ +[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 diff --git a/tests/data/journals/mostlyreadabledates.journal b/tests/data/journals/mostlyreadabledates.journal new file mode 100644 index 00000000..625ebcf2 --- /dev/null +++ b/tests/data/journals/mostlyreadabledates.journal @@ -0,0 +1,8 @@ +[2019-07-18 14:23] The first entry +Time machines are possible. I know, because I've built one in my garage. + +[2019-07-19 14:23] The second entry +I'm going to activate the machine. Nobody knows what comes next after this. Or before this? + +[2019-07 14:23] The third entry +I've crossed so many timelines. Is there any going back? diff --git a/tests/data/journals/multiline-tags.journal b/tests/data/journals/multiline-tags.journal new file mode 100644 index 00000000..1fb8706f --- /dev/null +++ b/tests/data/journals/multiline-tags.journal @@ -0,0 +1,7 @@ +[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 \ No newline at end of file diff --git a/tests/data/journals/multiline.journal b/tests/data/journals/multiline.journal new file mode 100644 index 00000000..294ed141 --- /dev/null +++ b/tests/data/journals/multiline.journal @@ -0,0 +1,5 @@ +[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. diff --git a/tests/data/journals/simple.journal b/tests/data/journals/simple.journal new file mode 100644 index 00000000..8336068e --- /dev/null +++ b/tests/data/journals/simple.journal @@ -0,0 +1,5 @@ +[2013-06-09 15:39] My first entry. +Everything is alright + +[2013-06-10 15:40] Life is good. +But I'm better. diff --git a/tests/data/journals/simple_jrnl-1-9-5.journal b/tests/data/journals/simple_jrnl-1-9-5.journal new file mode 100644 index 00000000..7bb6c5ac --- /dev/null +++ b/tests/data/journals/simple_jrnl-1-9-5.journal @@ -0,0 +1,13 @@ +2010-06-10 15:00 A life without chocolate is like a bad analogy. + +2013-06-10 15:40 He said "[this] is the best time to be alive". +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada +quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque +augue et venenatis facilisis. + +[2019-08-03 12:55] Some chat log or something + +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. diff --git a/tests/data/journals/simple_jrnl-1-9-5_little_endian_dates.journal b/tests/data/journals/simple_jrnl-1-9-5_little_endian_dates.journal new file mode 100644 index 00000000..328b23f4 --- /dev/null +++ b/tests/data/journals/simple_jrnl-1-9-5_little_endian_dates.journal @@ -0,0 +1,13 @@ +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". +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent malesuada +quis est ac dignissim. Aliquam dignissim rutrum pretium. Phasellus pellentesque +augue et venenatis facilisis. + +[03.08.2019 12:55] Some chat log or something + +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. diff --git a/tests/data/journals/tags-216.journal b/tests/data/journals/tags-216.journal new file mode 100644 index 00000000..08b6d630 --- /dev/null +++ b/tests/data/journals/tags-216.journal @@ -0,0 +1,2 @@ +[2013-06-10 15:40] I programmed for @OS/2. +Almost makes me want to go back to @C++, though. (Still better than @C#). diff --git a/tests/data/journals/tags-237.journal b/tests/data/journals/tags-237.journal new file mode 100644 index 00000000..be050652 --- /dev/null +++ b/tests/data/journals/tags-237.journal @@ -0,0 +1,3 @@ +[2014-07-22 11:11] This entry has an email. +@Newline tag should show as a tag. +Kyla's @email is kyla@clevelandunderdog.org and Guinness's is guinness@fortheloveofpits.org. diff --git a/tests/data/journals/tags.journal b/tests/data/journals/tags.journal new file mode 100644 index 00000000..a28f3159 --- /dev/null +++ b/tests/data/journals/tags.journal @@ -0,0 +1,8 @@ +[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 diff --git a/tests/data/journals/unreadabledates.journal b/tests/data/journals/unreadabledates.journal new file mode 100644 index 00000000..53ef1d60 --- /dev/null +++ b/tests/data/journals/unreadabledates.journal @@ -0,0 +1,5 @@ +[ashasd7zdskhz7asdkjasd] Entry subject +I've lost track of time. + +[sadfhakjsdf88sdf7sdff] Entry subject +Time has no meaning. diff --git a/tests/data/journals/work.journal b/tests/data/journals/work.journal new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/templates/sample.template b/tests/data/templates/sample.template new file mode 100644 index 00000000..a356d823 --- /dev/null +++ b/tests/data/templates/sample.template @@ -0,0 +1,19 @@ +--- +extension: txt +--- + +{% block journal %} +{% for entry in entries %} +{% include entry %} +{% endfor %} + +{% endblock %} + +{% block entry %} +{{ entry.title }} +{{ "-" * len(entry.title) }} + +{{ entry.body }} + +{% endblock %} +` diff --git a/tests/lib/fixtures.py b/tests/lib/fixtures.py new file mode 100644 index 00000000..a93a7e43 --- /dev/null +++ b/tests/lib/fixtures.py @@ -0,0 +1,201 @@ +# Copyright (C) 2012-2021 jrnl contributors +# License: https://www.gnu.org/licenses/gpl-3.0.html + +from collections import defaultdict +import os +from pathlib import Path +import tempfile + +from keyring import backend +from keyring import errors +from keyring import set_keyring +from pytest import fixture +import toml + +from jrnl.config import load_config + + +# --- Keyring --- # +@fixture +def keyring(): + set_keyring(NoKeyring()) + + +@fixture +def keyring_type(): + return "default" + + +class TestKeyring(backend.KeyringBackend): + """A test keyring that just stores its values in a hash""" + + priority = 1 + keys = defaultdict(dict) + + def set_password(self, servicename, username, password): + self.keys[servicename][username] = password + + def get_password(self, servicename, username): + return self.keys[servicename].get(username) + + def delete_password(self, servicename, username): + self.keys[servicename][username] = None + + +class NoKeyring(backend.KeyringBackend): + """A keyring that simulated an environment with no keyring backend.""" + + priority = 2 + keys = defaultdict(dict) + + def set_password(self, servicename, username, password): + raise errors.NoKeyringError + + def get_password(self, servicename, username): + raise errors.NoKeyringError + + def delete_password(self, servicename, username): + raise errors.NoKeyringError + + +class FailedKeyring(backend.KeyringBackend): + """A keyring that cannot be retrieved.""" + + priority = 2 + + def set_password(self, servicename, username, password): + raise errors.KeyringError + + def get_password(self, servicename, username): + raise errors.KeyringError + + def delete_password(self, servicename, username): + raise errors.KeyringError + + +# ----- Misc ----- # +@fixture +def cli_run(): + return {"status": 0, "stdout": None, "stderr": None} + + +@fixture +def mocks(): + return dict() + + +@fixture +def temp_dir(): + return tempfile.TemporaryDirectory() + + +@fixture +def working_dir(request): + return os.path.join(request.config.rootpath, "tests") + + +@fixture +def config_path(temp_dir): + os.chdir(temp_dir.name) + return temp_dir.name + "/jrnl.yaml" + + +@fixture +def toml_version(working_dir): + pyproject = os.path.join(working_dir, "..", "pyproject.toml") + pyproject_contents = toml.load(pyproject) + return pyproject_contents["tool"]["poetry"]["version"] + + +@fixture +def password(): + return "" + + +@fixture +def input_method(): + return "" + + +@fixture +def cache_dir(): + return {"exists": False, "path": ""} + + +@fixture +def str_value(): + return "" + + +@fixture +def command(): + return "" + + +@fixture +def should_not(): + return False + + +@fixture +def user_input(): + return "" + + +@fixture +def config_data(config_path): + return load_config(config_path) + + +@fixture +def journal_name(): + return None + + +@fixture +def which_output_stream(): + return None + + +@fixture +def editor_input(): + return None + + +@fixture +def num_args(): + return None + + +@fixture +def parsed_output(): + return {"lang": None, "obj": None} + + +@fixture +def editor_state(): + return { + "command": "", + "intent": {"method": "r", "input": None}, + "tmpfile": {"name": None, "content": None}, + } + + +@fixture +def editor(editor_state): + def _mock_editor(editor_command): + tmpfile = editor_command[-1] + + editor_state["command"] = editor_command + editor_state["tmpfile"]["name"] = tmpfile + + Path(tmpfile).touch() + with open(tmpfile, editor_state["intent"]["method"]) as f: + # Touch the file so jrnl knows it was edited + if editor_state["intent"]["input"] is not None: + f.write(editor_state["intent"]["input"]) + + file_content = f.read() + editor_state["tmpfile"]["content"] = file_content + + return _mock_editor diff --git a/tests/lib/given_steps.py b/tests/lib/given_steps.py new file mode 100644 index 00000000..649d44c5 --- /dev/null +++ b/tests/lib/given_steps.py @@ -0,0 +1,134 @@ +# Copyright (C) 2012-2021 jrnl contributors +# License: https://www.gnu.org/licenses/gpl-3.0.html + +from datetime import datetime +import json +import os +import random +import shutil +import string +from unittest.mock import MagicMock +from unittest.mock import patch +from xml.etree import ElementTree + +from keyring import set_keyring +from pytest_bdd import given +from pytest_bdd.parsers import parse + +from jrnl import __version__ +from jrnl.time import __get_pdt_calendar + +from .fixtures import FailedKeyring +from .fixtures import TestKeyring + + +@given(parse("we {editor_method} to the editor if opened\n{editor_input}")) +@given(parse("we {editor_method} nothing to the editor if opened")) +def we_enter_editor(editor_method, editor_input, editor_state): + file_method = editor_state["intent"]["method"] + if editor_method == "write": + file_method = "w+" + elif editor_method == "append": + file_method = "a+" + else: + assert False, f"Method '{editor_method}' not supported" + + editor_state["intent"] = {"method": file_method, "input": editor_input} + + +@given(parse('now is ""')) +@given(parse('now is "{date_str}"')) +def now_is_str(date_str, mocks): + class DatetimeMagicMock(MagicMock): + # needed because jrnl does some reflection on datetime + def __instancecheck__(self, subclass): + return isinstance(subclass, datetime) + + def mocked_now(tz=None): + now = datetime.strptime(date_str, "%Y-%m-%d %I:%M:%S %p") + + if tz: + time_zone = datetime.utcnow().astimezone().tzinfo + now = now.replace(tzinfo=time_zone) + + return now + + # jrnl uses two different classes to parse dates, so both must be mocked + datetime_mock = DatetimeMagicMock(wraps=datetime) + datetime_mock.now.side_effect = mocked_now + + pdt = __get_pdt_calendar() + calendar_mock = MagicMock(wraps=pdt) + calendar_mock.parse.side_effect = lambda date_str_input: pdt.parse( + date_str_input, mocked_now() + ) + + mocks["datetime"] = patch("datetime.datetime", new=datetime_mock) + mocks["calendar_parse"] = patch( + "jrnl.time.__get_pdt_calendar", return_value=calendar_mock + ) + + +@given("we have a keyring", target_fixture="keyring") +@given(parse("we have a {keyring_type} keyring"), target_fixture="keyring") +def we_have_type_of_keyring(keyring_type): + if keyring_type == "failed": + set_keyring(FailedKeyring()) + else: + set_keyring(TestKeyring()) + + +@given(parse('we use the config "{config_file}"'), target_fixture="config_path") +@given('we use the config ""', target_fixture="config_path") +def we_use_the_config(config_file, temp_dir, working_dir): + # Move into temp dir as cwd + os.chdir(temp_dir.name) + + # Copy the config file over + config_source = os.path.join(working_dir, "data", "configs", config_file) + config_dest = os.path.join(temp_dir.name, config_file) + shutil.copy2(config_source, config_dest) + + # @todo make this only copy some journals over + # Copy all of the journals over + journal_source = os.path.join(working_dir, "data", "journals") + journal_dest = os.path.join(temp_dir.name, "features", "journals") + shutil.copytree(journal_source, journal_dest) + + # @todo get rid of this by using default config values + # merge in version number + if config_file.endswith("yaml") and os.path.exists(config_dest): + # Add jrnl version to file for 2.x journals + with open(config_dest, "a") as cf: + cf.write("version: {}".format(__version__)) + + return config_dest + + +@given(parse('we use the password "{pw}" if prompted'), target_fixture="password") +def use_password_forever(pw): + return pw + + +@given("we create a cache directory", target_fixture="cache_dir") +def create_cache_dir(temp_dir): + random_str = "".join(random.choices(string.ascii_uppercase + string.digits, k=20)) + + dir_path = os.path.join(temp_dir.name, "cache_" + random_str) + os.mkdir(dir_path) + return {"exists": True, "path": dir_path} + + +@given(parse("we parse the output as {language_name}"), target_fixture="parsed_output") +def parse_output_as_language(cli_run, language_name): + language_name = language_name.upper() + actual_output = cli_run["stdout"] + + if language_name == "XML": + parsed_output = ElementTree.fromstring(actual_output) + elif language_name == "JSON": + parsed_output = json.loads(actual_output) + else: + assert False, f"Language name {language_name} not recognized" + + return {"lang": language_name, "obj": parsed_output} diff --git a/tests/lib/helpers.py b/tests/lib/helpers.py new file mode 100644 index 00000000..7d089597 --- /dev/null +++ b/tests/lib/helpers.py @@ -0,0 +1,40 @@ +# Copyright (C) 2012-2021 jrnl contributors +# License: https://www.gnu.org/licenses/gpl-3.0.html + +import os + + +def does_directory_contain_files(file_list, directory_path): + if not os.path.isdir(directory_path): + return False + + for file in file_list.split("\n"): + fullpath = directory_path + "/" + file + if not os.path.isfile(fullpath): + return False + + return True + + +def parse_should_or_should_not(should_or_should_not): + if should_or_should_not == "should": + return True + elif should_or_should_not == "should not": + return False + else: + raise Exception( + "should_or_should_not valid values are 'should' or 'should not'" + ) + + +def assert_equal_tags_ignoring_order( + actual_line, expected_line, actual_content, expected_content +): + actual_tags = set(tag.strip() for tag in actual_line[len("tags: ") :].split(",")) + expected_tags = set( + tag.strip() for tag in expected_line[len("tags: ") :].split(",") + ) + assert actual_tags == expected_tags, [ + [actual_tags, expected_tags], + [expected_content, actual_content], + ] diff --git a/tests/lib/then_steps.py b/tests/lib/then_steps.py new file mode 100644 index 00000000..b56c203f --- /dev/null +++ b/tests/lib/then_steps.py @@ -0,0 +1,331 @@ +# Copyright (C) 2012-2021 jrnl contributors +# License: https://www.gnu.org/licenses/gpl-3.0.html + +import json +import os +import re +from xml.etree import ElementTree + +from pytest_bdd import then +from pytest_bdd.parsers import parse +import yaml + +from jrnl.config import scope_config + +from .helpers import assert_equal_tags_ignoring_order +from .helpers import does_directory_contain_files +from .helpers import parse_should_or_should_not + + +@then("we should get no error") +def should_get_no_error(cli_run): + assert cli_run["status"] == 0, cli_run["status"] + + +@then(parse('the output should match "{regex}"')) +def output_should_match(regex, cli_run): + out = cli_run["stdout"] + matches = re.findall(regex, out) + assert matches, f"\nRegex didn't match:\n{regex}\n{str(out)}\n{str(matches)}" + + +@then(parse("the output should contain\n{expected_output}")) +@then(parse('the output should contain "{expected_output}"')) +@then('the output should contain ""') +@then(parse("the {which_output_stream} output should contain\n{expected_output}")) +@then(parse('the {which_output_stream} output should contain "{expected_output}"')) +def output_should_contain(expected_output, which_output_stream, cli_run): + assert expected_output + if which_output_stream is None: + assert (expected_output in cli_run["stdout"]) or ( + expected_output in cli_run["stderr"] + ) + + elif which_output_stream == "standard": + assert expected_output in cli_run["stdout"] + + elif which_output_stream == "error": + assert expected_output in cli_run["stderr"] + + else: + assert expected_output in cli_run[which_output_stream] + + +@then(parse("the output should not contain\n{expected_output}")) +@then(parse('the output should not contain "{expected_output}"')) +@then('the output should not contain ""') +def output_should_not_contain(expected_output, cli_run): + assert expected_output not in cli_run["stdout"] + + +@then(parse("the output should be\n{expected_output}")) +@then(parse('the output should be "{expected_output}"')) +@then('the output should be ""') +def output_should_be(expected_output, cli_run): + actual = cli_run["stdout"].strip() + expected = expected_output.strip() + assert expected == actual + + +@then("the output should be empty") +def output_should_be_empty(cli_run): + actual = cli_run["stdout"].strip() + assert actual == "" + + +@then('the output should contain the date ""') +def output_should_contain_date(date, cli_run): + assert date and date in cli_run["stdout"] + + +@then("the output should contain pyproject.toml version") +def output_should_contain_version(cli_run, toml_version): + out = cli_run["stdout"] + assert toml_version in out, toml_version + + +@then(parse('we should see the message "{text}"')) +def should_see_the_message(text, cli_run): + out = cli_run["stderr"] + assert text in out, [text, out] + + +@then( + parse( + 'the config for journal "{journal_name}" {should_or_should_not} contain "{some_yaml}"' + ) +) +@then( + parse( + 'the config for journal "{journal_name}" {should_or_should_not} contain\n{some_yaml}' + ) +) +@then(parse('the config {should_or_should_not} contain "{some_yaml}"')) +@then(parse("the config {should_or_should_not} contain\n{some_yaml}")) +def config_var(config_data, journal_name, should_or_should_not, some_yaml): + we_should = parse_should_or_should_not(should_or_should_not) + + actual = config_data + if journal_name: + actual = actual["journals"][journal_name] + + expected = yaml.load(some_yaml, Loader=yaml.FullLoader) + + actual_slice = actual + if type(actual) is dict: + actual_slice = {key: actual.get(key, None) for key in expected.keys()} + + if we_should: + assert expected == actual_slice + else: + assert expected != actual_slice + + +@then("we should be prompted for a password") +def password_was_called(cli_run): + assert cli_run["mocks"]["getpass"].called + + +@then("we should not be prompted for a password") +def password_was_not_called(cli_run): + assert not cli_run["mocks"]["getpass"].called + + +@then(parse("the cache directory should contain the files\n{file_list}")) +def assert_dir_contains_files(file_list, cache_dir): + assert does_directory_contain_files(file_list, cache_dir["path"]) + + +@then(parse("the journal directory should contain\n{file_list}")) +def journal_directory_should_contain(config_data, file_list): + scoped_config = scope_config(config_data, "default") + + assert does_directory_contain_files(file_list, scoped_config["journal"]) + + +@then(parse('journal "{journal_name}" should not exist')) +def journal_directory_should_not_exist(config_data, journal_name): + scoped_config = scope_config(config_data, journal_name) + + assert not does_directory_contain_files( + scoped_config["journal"], "." + ), f'Journal "{journal_name}" does exist' + + +@then(parse("the journal {should_or_should_not} exist")) +def journal_should_not_exist(config_data, should_or_should_not): + scoped_config = scope_config(config_data, "default") + expected_path = scoped_config["journal"] + + contains_files = does_directory_contain_files(expected_path, ".") + + if should_or_should_not == "should": + assert contains_files + elif should_or_should_not == "should not": + assert not contains_files + else: + raise Exception( + "should_or_should_not valid values are 'should' or 'should not'" + ) + + +@then(parse('the content of file "{file_path}" in the cache should be\n{file_content}')) +def content_of_file_should_be(file_path, file_content, cache_dir): + assert cache_dir["exists"] + expected_content = file_content.strip().splitlines() + + with open(os.path.join(cache_dir["path"], file_path), "r") as f: + actual_content = f.read().strip().splitlines() + + for actual_line, expected_line in zip(actual_content, expected_content): + if actual_line.startswith("tags: ") and expected_line.startswith("tags: "): + assert_equal_tags_ignoring_order( + actual_line, expected_line, actual_content, expected_content + ) + else: + assert actual_line.strip() == expected_line.strip(), [ + [actual_line.strip(), expected_line.strip()], + [actual_content, expected_content], + ] + + +@then(parse("the cache should contain the files\n{file_list}")) +def cache_dir_contains_files(file_list, cache_dir): + assert cache_dir["exists"] + + actual_files = os.listdir(cache_dir["path"]) + expected_files = file_list.split("\n") + + # sort to deal with inconsistent default file ordering on different OS's + actual_files.sort() + expected_files.sort() + + assert actual_files == expected_files, [actual_files, expected_files] + + +@then(parse("the output should be valid {language_name}")) +def assert_output_is_valid_language(cli_run, language_name): + language_name = language_name.upper() + if language_name == "XML": + xml_tree = ElementTree.fromstring(cli_run["stdout"]) + assert xml_tree, "Invalid XML" + elif language_name == "JSON": + assert json.loads(cli_run["stdout"]), "Invalid JSON" + else: + assert False, f"Language name {language_name} not recognized" + + +@then(parse('"{node_name}" in the parsed output should have {number:d} elements')) +def assert_parsed_output_item_count(node_name, number, parsed_output): + lang = parsed_output["lang"] + obj = parsed_output["obj"] + + if lang == "XML": + xml_node_names = (node.tag for node in obj) + assert node_name in xml_node_names, str(list(xml_node_names)) + + actual_entry_count = len(obj.find(node_name)) + assert actual_entry_count == number, actual_entry_count + + elif lang == "JSON": + my_obj = obj + + for node in node_name.split("."): + try: + my_obj = my_obj[int(node)] + except ValueError: + assert node in my_obj + my_obj = my_obj[node] + + assert len(my_obj) == number, len(my_obj) + + else: + assert False, f"Language name {lang} not recognized" + + +@then(parse('"{field_name}" in the parsed output should {comparison}\n{expected_keys}')) +def assert_output_field_content(field_name, comparison, expected_keys, parsed_output): + lang = parsed_output["lang"] + obj = parsed_output["obj"] + expected_keys = expected_keys.split("\n") + if len(expected_keys) == 1: + expected_keys = expected_keys[0] + + if lang == "XML": + xml_node_names = (node.tag for node in obj) + assert field_name in xml_node_names, str(list(xml_node_names)) + + if field_name == "tags": + actual_tags = set(t.attrib["name"] for t in obj.find("tags")) + assert set(actual_tags) == set(expected_keys), [ + actual_tags, + set(expected_keys), + ] + else: + assert False, "This test only works for tags in XML" + + elif lang == "JSON": + my_obj = obj + + for node in field_name.split("."): + try: + my_obj = my_obj[int(node)] + except ValueError: + assert node in my_obj, [my_obj.keys(), node] + my_obj = my_obj[node] + + if comparison == "be": + if type(my_obj) is str: + assert expected_keys == my_obj, [my_obj, expected_keys] + else: + assert set(expected_keys) == set(my_obj), [ + set(my_obj), + set(expected_keys), + ] + elif comparison == "contain": + if type(my_obj) is str: + assert expected_keys in my_obj, [my_obj, expected_keys] + else: + assert all(elem in my_obj for elem in expected_keys), [ + my_obj, + expected_keys, + ] + else: + assert False, f"Language name {lang} not recognized" + + +@then(parse('there should be {number:d} "{item}" elements')) +def count_elements(number, item, cli_run): + actual_output = cli_run["stdout"] + xml_tree = ElementTree.fromstring(actual_output) + assert len(xml_tree.findall(".//" + item)) == number + + +@then(parse("the editor should have been called")) +@then(parse("the editor should have been called with {num_args} arguments")) +def count_editor_args(num_args, cli_run, editor_state): + assert cli_run["mocks"]["editor"].called + + if isinstance(num_args, int): + assert len(editor_state["command"]) == int(num_args) + + +@then(parse('the editor filename should end with "{suffix}"')) +def editor_filename_suffix(suffix, editor_state): + editor_filename = editor_state["tmpfile"]["name"] + + assert editor_state["tmpfile"]["name"].endswith(suffix), (editor_filename, suffix) + + +@then(parse('the editor file content should {comparison} "{str_value}"')) +@then(parse("the editor file content should {comparison} empty")) +@then(parse("the editor file content should {comparison}\n{str_value}")) +def contains_editor_file(comparison, str_value, editor_state): + content = editor_state["tmpfile"]["content"] + # content = f'\n"""\n{content}\n"""\n' + if comparison == "be": + assert content == str_value + elif comparison == "contain": + assert str_value in content + else: + assert False, f"Comparison '{comparison}' not supported" diff --git a/tests/lib/when_steps.py b/tests/lib/when_steps.py new file mode 100644 index 00000000..642249e9 --- /dev/null +++ b/tests/lib/when_steps.py @@ -0,0 +1,110 @@ +# Copyright (C) 2012-2021 jrnl contributors +# License: https://www.gnu.org/licenses/gpl-3.0.html + +from contextlib import ExitStack +import os +from unittest.mock import patch + +from pytest_bdd import parsers +from pytest_bdd import when +from pytest_bdd.parsers import parse + +from jrnl.cli import cli +from jrnl.os_compat import split_args + + +@when(parse('we change directory to "{directory_name}"')) +def when_we_change_directory(directory_name): + if not os.path.isdir(directory_name): + os.mkdir(directory_name) + + os.chdir(directory_name) + + +@when(parse('we run "jrnl {command}" and {input_method}\n{user_input}')) +@when( + parsers.re( + 'we run "jrnl (?P[^"]+)" and (?Penter|pipe) "(?P[^"]+)"' + ) +) +@when(parse('we run "jrnl" and {input_method} "{user_input}"')) +@when(parse('we run "jrnl {command}"')) +@when('we run "jrnl "') +@when('we run "jrnl"') +def we_run( + command, + config_path, + user_input, + cli_run, + capsys, + password, + cache_dir, + editor, + keyring, + input_method, + mocks, +): + assert input_method in ["", "enter", "pipe"] + is_tty = input_method != "pipe" + + if cache_dir["exists"]: + command = command.format(cache_dir=cache_dir["path"]) + + args = split_args(command) + status = 0 + + if user_input: + user_input = user_input.splitlines() if is_tty else [user_input] + + if password: + password = password.splitlines() + + if not password and user_input: + password = user_input + + with ExitStack() as stack: + + stack.enter_context(patch("sys.argv", ["jrnl"] + args)) + + mock_stdin = stack.enter_context( + patch("sys.stdin.read", side_effect=user_input) + ) + stack.enter_context(patch("sys.stdin.isatty", return_value=is_tty)) + mock_input = stack.enter_context( + patch("builtins.input", side_effect=user_input) + ) + mock_getpass = stack.enter_context( + patch("getpass.getpass", side_effect=password) + ) + + if "datetime" in mocks: + stack.enter_context(mocks["datetime"]) + stack.enter_context(mocks["calendar_parse"]) + + stack.enter_context( + patch("jrnl.install.get_config_path", return_value=config_path) + ) + stack.enter_context( + patch("jrnl.config.get_config_path", return_value=config_path) + ) + mock_editor = stack.enter_context(patch("subprocess.call", side_effect=editor)) + + try: + cli(args) + except StopIteration: + # This happens when input is expected, but don't have any input left + pass + except SystemExit as e: + status = e.code + + captured = capsys.readouterr() + + cli_run["status"] = status + cli_run["stdout"] = captured.out + cli_run["stderr"] = captured.err + cli_run["mocks"] = { + "stdin": mock_stdin, + "input": mock_input, + "getpass": mock_getpass, + "editor": mock_editor, + } diff --git a/tests/test_color.py b/tests/unit/test_color.py similarity index 86% rename from tests/test_color.py rename to tests/unit/test_color.py index 14dc7938..527b8e89 100644 --- a/tests/test_color.py +++ b/tests/unit/test_color.py @@ -1,7 +1,8 @@ +from colorama import Fore +from colorama import Style import pytest from jrnl.color import colorize -from colorama import Fore, Style @pytest.fixture() diff --git a/tests/test_display.py b/tests/unit/test_display.py similarity index 99% rename from tests/test_display.py rename to tests/unit/test_display.py index 72a9c451..921d1631 100644 --- a/tests/test_display.py +++ b/tests/unit/test_display.py @@ -1,7 +1,9 @@ import argparse -import jrnl -import pytest from unittest import mock + +import pytest + +import jrnl from jrnl.jrnl import _display_search_results diff --git a/tests/test_exception.py b/tests/unit/test_exception.py similarity index 100% rename from tests/test_exception.py rename to tests/unit/test_exception.py diff --git a/tests/test_export.py b/tests/unit/test_export.py similarity index 100% rename from tests/test_export.py rename to tests/unit/test_export.py index 0f494f79..8bc1b410 100644 --- a/tests/test_export.py +++ b/tests/unit/test_export.py @@ -1,8 +1,8 @@ +import pytest + from jrnl.exception import JrnlError from jrnl.plugins.fancy_exporter import check_provided_linewrap_viability -import pytest - @pytest.fixture() def datestr(): diff --git a/tests/test_install.py b/tests/unit/test_install.py similarity index 99% rename from tests/test_install.py rename to tests/unit/test_install.py index 31ec150c..bc36b927 100644 --- a/tests/test_install.py +++ b/tests/unit/test_install.py @@ -1,6 +1,7 @@ -from unittest import mock -import pytest import sys +from unittest import mock + +import pytest @pytest.mark.filterwarnings( diff --git a/tests/test_os_compat.py b/tests/unit/test_os_compat.py similarity index 99% rename from tests/test_os_compat.py rename to tests/unit/test_os_compat.py index f7c058f1..02d54716 100644 --- a/tests/test_os_compat.py +++ b/tests/unit/test_os_compat.py @@ -1,8 +1,9 @@ from unittest import mock + import pytest -from jrnl.os_compat import on_windows from jrnl.os_compat import on_posix +from jrnl.os_compat import on_windows from jrnl.os_compat import split_args diff --git a/tests/test_override.py b/tests/unit/test_override.py similarity index 87% rename from tests/test_override.py rename to tests/unit/test_override.py index 32ec0595..d22709f4 100644 --- a/tests/test_override.py +++ b/tests/unit/test_override.py @@ -1,12 +1,10 @@ import pytest -from jrnl.override import ( - apply_overrides, - _recursively_apply, - _get_config_node, - _get_key_and_value_from_pair, - _convert_dots_to_list, -) +from jrnl.override import _convert_dots_to_list +from jrnl.override import _get_config_node +from jrnl.override import _get_key_and_value_from_pair +from jrnl.override import _recursively_apply +from jrnl.override import apply_overrides @pytest.fixture() @@ -56,7 +54,7 @@ def test_recursively_apply(): def test_get_config_node(minimal_config): assert len(minimal_config.keys()) == 4 assert _get_config_node(minimal_config, "editor") == "vim" - assert _get_config_node(minimal_config, "display_format") == None + assert _get_config_node(minimal_config, "display_format") is None def test_get_kv_from_pair(): diff --git a/tests/test_parse_args.py b/tests/unit/test_parse_args.py similarity index 99% rename from tests/test_parse_args.py rename to tests/unit/test_parse_args.py index 4b140fc1..0725d33d 100644 --- a/tests/test_parse_args.py +++ b/tests/unit/test_parse_args.py @@ -283,10 +283,10 @@ class TestDeserialization: assert cfg["linewrap"] == 23 cfg = make_yaml_valid_dict(["encrypt", "false"]) - assert cfg["encrypt"] == False + assert cfg["encrypt"] is False cfg = make_yaml_valid_dict(["editor", "vi -c startinsert"]) assert cfg["editor"] == "vi -c startinsert" cfg = make_yaml_valid_dict(["highlight", "true"]) - assert cfg["highlight"] == True + assert cfg["highlight"] is True diff --git a/tests/test_time.py b/tests/unit/test_time.py similarity index 100% rename from tests/test_time.py rename to tests/unit/test_time.py