diff --git a/screenpy_requests/actions/__init__.py b/screenpy_requests/actions/__init__.py index 5f7c6c0..acb2c73 100644 --- a/screenpy_requests/actions/__init__.py +++ b/screenpy_requests/actions/__init__.py @@ -23,11 +23,11 @@ def generate_send_method_class(method: str) -> Type[APIMethodAction]: """ class SendMETHODRequest: - "Will be programmatically replaced." + """Will be programmatically replaced.""" @staticmethod def to(url: str) -> SendAPIRequest: - "Will be programmatically replaced." + """Will be programmatically replaced.""" return SendAPIRequest(method, url) SendMETHODRequest.__doc__ = f"""Send a {method} request to a URL. diff --git a/screenpy_requests/questions/body_of_the_last_response.py b/screenpy_requests/questions/body_of_the_last_response.py index b561b29..8852e62 100644 --- a/screenpy_requests/questions/body_of_the_last_response.py +++ b/screenpy_requests/questions/body_of_the_last_response.py @@ -2,6 +2,8 @@ Investigate the body of the last API response received by the Actor. """ +from __future__ import annotations + from json.decoder import JSONDecodeError from typing import Union @@ -11,35 +13,72 @@ from ..abilities import MakeAPIRequests +subscripts = Union[str, int, slice] + class BodyOfTheLastResponse: """Ask about the body of the last API response received by the Actor. + If you are expecting a JSON response, this Question can be indexed to + return only a section of that response. See the examples below. + + If you are expecting a text response, you can also slice the text. + Abilities Required: :class:`~screenpy_requests.abilities.MakeAPIRequests` Examples:: + # JSON response the_actor.should( See.the(BodyOfTheLastResponse(), ContainsTheEntry(play="Hamlet")) ) + # JSON response, picked + the_actor.should( + See.the( + BodyOfTheLastResponse()["users"][0]["first_name"], + ReadsExactly("Monty") + ), + ) + + # text response the_actor.should( See.the(BodyOfTheLastResponse(), ReadsExactly("To be, or not to be")) ) + + # text response, sliced + the_actor.should( + See.the(BodyOfTheLastResponse()[6:12], ReadsExactly("Python")) + ) """ + body_parts: list[subscripts] + + def __getitem__(self, key: subscripts) -> BodyOfTheLastResponse: + self.body_parts.append(key) + return self + + def __init__(self) -> None: + self.body_parts = [] + def describe(self) -> str: """Describe the Question..""" return "The body of the last response." @beat("{} examines the body of the last response they received.") - def answered_by(self, the_actor: Actor) -> Union[dict, str]: + def answered_by(self, the_actor: Actor) -> dict | str: """Direct the Actor to investigate the body of the last response.""" responses = the_actor.ability_to(MakeAPIRequests).responses if len(responses) < 1: raise UnableToAnswer(f"{the_actor} has not yet received any API responses.") try: - return responses[-1].json() + response = responses[-1].json() + for part in self.body_parts: + response = response[part] + return response except JSONDecodeError: - return responses[-1].text + response = responses[-1].text + for part in self.body_parts: + response = response[part] + return response diff --git a/tests/conftest.py b/tests/conftest.py index 1982591..01c11db 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,6 @@ from unittest import mock import pytest - from screenpy import AnActor from screenpy_requests.abilities import MakeAPIRequests diff --git a/tests/test_actions.py b/tests/test_actions.py index 856e705..1c9ae6d 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -1,4 +1,5 @@ import logging + import pytest from screenpy_requests.abilities import MakeAPIRequests @@ -13,8 +14,8 @@ SendPATCHRequest, SendPOSTRequest, SendPUTRequest, - generate_send_method_class, SetHeaders, + generate_send_method_class, ) diff --git a/tests/test_questions.py b/tests/test_questions.py index d39748d..cd40579 100644 --- a/tests/test_questions.py +++ b/tests/test_questions.py @@ -2,7 +2,6 @@ from unittest import mock import pytest - from requests.cookies import RequestsCookieJar from screenpy.exceptions import UnableToAnswer @@ -53,6 +52,32 @@ def test_ask_for_body_of_the_last_response(self, APITester): assert BodyOfTheLastResponse().answered_by(APITester) == test_json + def test_stores_index_path(self): + botlr = BodyOfTheLastResponse()["shuffled"]["off"][10]["mortal"]["coils"] + + assert botlr.body_parts == ["shuffled", "off", 10, "mortal", "coils"] + + def test_digs_into_json(self, APITester): + test_json = {"plays": [{"name": "Hamlet"}]} + fake_response = mock.Mock() + fake_response.json.return_value = test_json + mocked_mar = APITester.ability_to(MakeAPIRequests) + mocked_mar.responses = [fake_response] + + botlr = BodyOfTheLastResponse()["plays"][0]["name"] + + assert botlr.answered_by(APITester) == "Hamlet" + + def test_slices_text(self, APITester): + test_text = "My favorite play is Hamlet." + fake_response = mock.Mock() + fake_response.json.return_value = test_text + mocked_mar = APITester.ability_to(MakeAPIRequests) + mocked_mar.responses = [fake_response] + + assert BodyOfTheLastResponse()[-7:-1].answered_by(APITester) == "Hamlet" + + class TestCookies: def test_can_be_instantiated(self):