mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Implement ExitStack to handle mocks in pytest-bdd
- Fix failing DayOne test - Make format and clean up extraneous comment Co-authored-by: Jonathan Wren <jonathan@nowandwren.com>
This commit is contained in:
parent
0c8efd5331
commit
fd349fb0fc
2 changed files with 56 additions and 33 deletions
|
@ -196,16 +196,14 @@ Feature: Writing new entries.
|
||||||
And "entries.0.creator.software_agent" in the parsed output should contain
|
And "entries.0.creator.software_agent" in the parsed output should contain
|
||||||
jrnl
|
jrnl
|
||||||
|
|
||||||
# fails when system time is UTC (as on Travis-CI)
|
|
||||||
# @skip
|
|
||||||
Scenario: Title with an embedded period on DayOne journal
|
Scenario: Title with an embedded period on DayOne journal
|
||||||
Given we use the config "dayone.yaml"
|
Given we use the config "dayone.yaml"
|
||||||
When we run "jrnl 04-24-2014: "Ran 6.2 miles today in 1:02:03. I'm feeling sore because I forgot to stretch.""
|
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"
|
Then we should see the message "Entry added"
|
||||||
When we run "jrnl -1"
|
When we run "jrnl -1"
|
||||||
Then the output should be
|
Then the output should be
|
||||||
2014-04-24 09:00 Ran 6.2 miles today in 1:02:03.
|
2014-04-24 09:00 Ran 6.2 miles today in 1:02:03.
|
||||||
| I'm feeling sore because I forgot to stretch.
|
| I am feeling sore because I forgot to stretch.
|
||||||
|
|
||||||
Scenario: Opening an folder that's not a DayOne folder should treat as folder journal
|
Scenario: Opening an folder that's not a DayOne folder should treat as folder journal
|
||||||
Given we use the config "empty_folder.yaml"
|
Given we use the config "empty_folder.yaml"
|
||||||
|
|
|
@ -5,6 +5,7 @@ import json
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from contextlib import ExitStack
|
||||||
from keyring import backend
|
from keyring import backend
|
||||||
from keyring import set_keyring
|
from keyring import set_keyring
|
||||||
from keyring import errors
|
from keyring import errors
|
||||||
|
@ -121,6 +122,11 @@ def cli_run():
|
||||||
return {"status": 0, "stdout": None, "stderr": None}
|
return {"status": 0, "stdout": None, "stderr": None}
|
||||||
|
|
||||||
|
|
||||||
|
@fixture
|
||||||
|
def mocks():
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
|
||||||
@fixture
|
@fixture
|
||||||
def temp_dir():
|
def temp_dir():
|
||||||
return tempfile.TemporaryDirectory()
|
return tempfile.TemporaryDirectory()
|
||||||
|
@ -154,11 +160,6 @@ def input_method():
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@fixture
|
|
||||||
def now_date():
|
|
||||||
return {"datetime": datetime, "calendar_parse": __get_pdt_calendar()}
|
|
||||||
|
|
||||||
|
|
||||||
@fixture
|
@fixture
|
||||||
def cache_dir():
|
def cache_dir():
|
||||||
return {"exists": False, "path": ""}
|
return {"exists": False, "path": ""}
|
||||||
|
@ -268,27 +269,37 @@ def we_enter_editor(editor_method, editor_input, editor_state):
|
||||||
editor_state["intent"] = {"method": file_method, "input": editor_input}
|
editor_state["intent"] = {"method": file_method, "input": editor_input}
|
||||||
|
|
||||||
|
|
||||||
@given(parse('now is "<date_str>"'), target_fixture="now_date")
|
@given(parse('now is "<date_str>"'))
|
||||||
@given(parse('now is "{date_str}"'), target_fixture="now_date")
|
@given(parse('now is "{date_str}"'))
|
||||||
def now_is_str(date_str):
|
def now_is_str(date_str, mocks):
|
||||||
class DatetimeMagicMock(MagicMock):
|
class DatetimeMagicMock(MagicMock):
|
||||||
# needed because jrnl does some reflection on datetime
|
# needed because jrnl does some reflection on datetime
|
||||||
def __instancecheck__(self, subclass):
|
def __instancecheck__(self, subclass):
|
||||||
return isinstance(subclass, datetime)
|
return isinstance(subclass, datetime)
|
||||||
|
|
||||||
my_date = datetime.strptime(date_str, "%Y-%m-%d %I:%M:%S %p")
|
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
|
# jrnl uses two different classes to parse dates, so both must be mocked
|
||||||
datetime_mock = DatetimeMagicMock(wraps=datetime)
|
datetime_mock = DatetimeMagicMock(wraps=datetime)
|
||||||
datetime_mock.now.return_value = my_date
|
datetime_mock.now.side_effect = mocked_now
|
||||||
|
|
||||||
pdt = __get_pdt_calendar()
|
pdt = __get_pdt_calendar()
|
||||||
calendar_mock = MagicMock(wraps=pdt)
|
calendar_mock = MagicMock(wraps=pdt)
|
||||||
calendar_mock.parse.side_effect = lambda date_str_input: pdt.parse(
|
calendar_mock.parse.side_effect = lambda date_str_input: pdt.parse(
|
||||||
date_str_input, my_date
|
date_str_input, mocked_now()
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"datetime": datetime_mock, "calendar_parse": calendar_mock}
|
mocks["datetime"] = patch("datetime.datetime", new=datetime_mock)
|
||||||
|
mocks["calendar_parse"] = patch(
|
||||||
|
"jrnl.time.__get_pdt_calendar", return_value=calendar_mock
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@then(parse("the editor should have been called"))
|
@then(parse("the editor should have been called"))
|
||||||
|
@ -381,9 +392,9 @@ def we_run(
|
||||||
password,
|
password,
|
||||||
cache_dir,
|
cache_dir,
|
||||||
editor,
|
editor,
|
||||||
now_date,
|
|
||||||
keyring,
|
keyring,
|
||||||
input_method,
|
input_method,
|
||||||
|
mocks,
|
||||||
):
|
):
|
||||||
assert input_method in ["", "enter", "pipe"]
|
assert input_method in ["", "enter", "pipe"]
|
||||||
is_tty = input_method != "pipe"
|
is_tty = input_method != "pipe"
|
||||||
|
@ -403,21 +414,36 @@ def we_run(
|
||||||
if not password and user_input:
|
if not password and user_input:
|
||||||
password = user_input
|
password = user_input
|
||||||
|
|
||||||
# fmt: off
|
with ExitStack() as stack:
|
||||||
# see: https://github.com/psf/black/issues/664
|
|
||||||
# @todo https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack
|
stack.enter_context(patch("sys.argv", ["jrnl"] + args))
|
||||||
with \
|
|
||||||
patch("sys.argv", ['jrnl'] + args), \
|
mock_stdin = stack.enter_context(
|
||||||
patch("sys.stdin.read", side_effect=user_input) as mock_stdin, \
|
patch("sys.stdin.read", side_effect=user_input)
|
||||||
patch("sys.stdin.isatty", return_value=is_tty), \
|
)
|
||||||
patch("builtins.input", side_effect=user_input) as mock_input, \
|
stack.enter_context(patch("sys.stdin.isatty", return_value=is_tty))
|
||||||
patch("getpass.getpass", side_effect=password) as mock_getpass, \
|
mock_input = stack.enter_context(
|
||||||
patch("datetime.datetime", new=now_date["datetime"]), \
|
patch("builtins.input", side_effect=user_input)
|
||||||
patch("jrnl.time.__get_pdt_calendar", return_value=now_date["calendar_parse"]), \
|
)
|
||||||
patch("jrnl.install.get_config_path", return_value=config_path), \
|
mock_getpass = stack.enter_context(
|
||||||
patch("jrnl.config.get_config_path", return_value=config_path), \
|
patch("getpass.getpass", side_effect=password)
|
||||||
patch("subprocess.call", side_effect=editor) as mock_editor \
|
)
|
||||||
: # @TODO: single point of truth for get_config_path (move from all calls from install to config)
|
|
||||||
|
if "datetime" in mocks:
|
||||||
|
stack.enter_context(mocks["datetime"])
|
||||||
|
stack.enter_context(mocks["calendar_parse"])
|
||||||
|
|
||||||
|
# stack.enter_context(patch("datetime.datetime", new=mocks["datetime"]))
|
||||||
|
# stack.enter_context(patch("jrnl.time.__get_pdt_calendar", return_value=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:
|
try:
|
||||||
cli(args)
|
cli(args)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
|
@ -425,7 +451,6 @@ def we_run(
|
||||||
pass
|
pass
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
status = e.code
|
status = e.code
|
||||||
# fmt: on
|
|
||||||
|
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue