jrnl/tests/lib/given_steps.py
samuelgregorovic ae009099ed
Add --config-file argument to use alternate config file at runtime (#1290)
* added new CLI argument option --config-file

* pass argument and fetch alt config file if specified

* argparse argument setting update

* argument alias --cf added

* documentation update - usage of CLI argument

* fixed name-clash + unit tests

* feature test added

* #1170-alternate-config-file: Auto stash before rebase of "refs/heads/#1170-alternate-config-file"

* Update docs/advanced.md

Co-authored-by: Jonathan Wren <jonathan@nowandwren.com>

* BDD tests added

* Begin migrating/rewording --cf tests in pytest-bdd.

Uses current directory instead of deep directory structure, but requires a given for each config file referenced

* Fix issue where specifying a config-file that needs to be upgraded ended up upgrading the user config file instead

* Uncomment and rework remaining tests for pytest-bdd instead of behave

* Fix copytree for Python 3.7 (which doesn't support dirs_exist_ok)

* Minor fixes to alternative config examples

* Remove behave tests (behave is no longer in use)

* Move config file unit test to unit test dir and use pytext path fixture instead of current directory to find test data

* Use explicit "given the config exists" for copying config files instead of shoehorning in "given we use the config" twice

* Change when/when to when/and

* Clarify scenarios and fix indentation

* Confirm primary config file isn't modified when encrypting/decrypting a journal in an alternate config file

* Remove try/except on copytree since I'm no longer using the same

Co-authored-by: Jonathan Wren <jonathan@nowandwren.com>
Co-authored-by: Micah Jerome Ellison <micah.jerome.ellison@gmail.com>
2021-11-06 14:12:34 -07:00

142 lines
4.8 KiB
Python

# 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 "<date_str>"'))
@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 "<config_file>"', 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('the config "{config_file}" exists'), target_fixture="config_path")
@given('the config "<config_file>" exists', target_fixture="config_path")
def config_exists(config_file, temp_dir, working_dir):
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)
@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}