jrnl/tests/step_defs/given_steps.py
Jonathan Wren c5a7d7027c Move pytest-bdd code into separate files
Now that all the tests are passing, this breaks them up into a few
different files to make everything more organized.

Note: Pyflakes is complaining about some unused import statements.
2021-07-03 15:49:18 -07:00

133 lines
4.4 KiB
Python

# Copyright (C) 2012-2021 jrnl contributors
# License: https://www.gnu.org/licenses/gpl-3.0.html
import json
import os
from datetime import datetime
from keyring import set_keyring
import random
import string
import shutil
from unittest.mock import patch
from unittest.mock import MagicMock
from xml.etree import ElementTree
from jrnl import __version__
from jrnl.time import __get_pdt_calendar
from pytest_bdd import given
from pytest_bdd.parsers import parse
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('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}