Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#6: add indexing to BodyOfTheLastResponse. #7

Merged
merged 9 commits into from
Feb 13, 2024
4 changes: 2 additions & 2 deletions screenpy_requests/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ def generate_send_method_class(method: str) -> Type[APIMethodAction]:
"""

class SendMETHODRequest:
"Will be programmatically replaced."
"""Will be programmatically replaced."""
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah also i had this fix here for months. Haha.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figure when we add ruff to the project, all these would be updated anyway.


@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.
Expand Down
45 changes: 42 additions & 3 deletions screenpy_requests/questions/body_of_the_last_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
1 change: 0 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from unittest import mock

import pytest

from screenpy import AnActor

from screenpy_requests.abilities import MakeAPIRequests
Expand Down
3 changes: 2 additions & 1 deletion tests/test_actions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging

import pytest

from screenpy_requests.abilities import MakeAPIRequests
Expand All @@ -13,8 +14,8 @@
SendPATCHRequest,
SendPOSTRequest,
SendPUTRequest,
generate_send_method_class,
SetHeaders,
generate_send_method_class,
)


Expand Down
27 changes: 26 additions & 1 deletion tests/test_questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from unittest import mock

import pytest

from requests.cookies import RequestsCookieJar
from screenpy.exceptions import UnableToAnswer

Expand Down Expand Up @@ -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"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a slice here.


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):
Expand Down
Loading