diff --git a/src/syrupy/assertion.py b/src/syrupy/assertion.py index 607c56ce..48e57399 100644 --- a/src/syrupy/assertion.py +++ b/src/syrupy/assertion.py @@ -1,4 +1,5 @@ import traceback +from collections import namedtuple from dataclasses import ( dataclass, field, @@ -15,6 +16,7 @@ ) from .exceptions import SnapshotDoesNotExist +from .extensions.amber.serializer import Repr if TYPE_CHECKING: from .extensions.base import AbstractSyrupyExtension @@ -54,8 +56,6 @@ class SnapshotAssertion: test_location: "PyTestLocation" update_snapshots: bool - name: str = "snapshot" - _exclude: Optional["PropertyFilter"] = field( init=False, default=None, @@ -76,6 +76,9 @@ class SnapshotAssertion: init=False, default_factory=dict, ) + _execution_name_index: Dict["SnapshotIndex", int] = field( + init=False, default_factory=dict + ) _matcher: Optional["PropertyMatcher"] = field( init=False, default=None, @@ -104,7 +107,7 @@ def num_executions(self) -> int: return int(self._executions) @property - def executions(self) -> Dict[int, AssertionResult]: + def executions(self) -> Dict[int, "AssertionResult"]: return self._execution_results @property @@ -113,6 +116,44 @@ def index(self) -> "SnapshotIndex": return self._custom_index return self.num_executions + @property + def name(self) -> str: + return self._custom_index or "snapshot" + + @property + def __repr(self) -> "SerializableData": + SnapshotAssertionRepr = namedtuple( # type: ignore + "SnapshotAssertion", ["name", "num_executions"] + ) + assertion_result = self.executions.get( + (self._custom_index and self._execution_name_index.get(self._custom_index)) + or self.num_executions - 1 + ) + return ( + Repr(str(assertion_result.final_data)) + if assertion_result + else SnapshotAssertionRepr( + name=self.name, + num_executions=self.num_executions, + ) + ) + + @property + def __matcher(self) -> "PropertyMatcher": + """ + Get matcher that replaces `SnapshotAssertion` with one that can be serialized + """ + + def _matcher(**kwargs: Any) -> Optional["SerializableData"]: + maybe_assertion = kwargs.get("data") + if isinstance(maybe_assertion, SnapshotAssertion): + return maybe_assertion.__repr + if self._matcher: + return self._matcher(**kwargs) + return maybe_assertion + + return _matcher + def use_extension( self, extension_class: Optional[Type["AbstractSyrupyExtension"]] = None ) -> "SnapshotAssertion": @@ -132,7 +173,7 @@ def assert_match(self, data: "SerializableData") -> None: def _serialize(self, data: "SerializableData") -> "SerializedData": return self.extension.serialize( - data, exclude=self._exclude, matcher=self._matcher + data, exclude=self._exclude, matcher=self.__matcher ) def get_assert_diff(self) -> List[str]: @@ -190,8 +231,8 @@ def __call__( self.__with_prop("_snapshot_diff", diff) return self - def __dir__(self) -> List[str]: - return ["name", "num_executions"] + def __repr__(self) -> str: + return str(self._serialize(self.__repr)) def __eq__(self, other: "SerializableData") -> bool: return self._assert(other) @@ -233,6 +274,7 @@ def _assert(self, data: "SerializableData") -> bool: finally: snapshot_created = snapshot_data is None and assertion_success snapshot_updated = matches is False and assertion_success + self._execution_name_index[self.index] = self._executions self._execution_results[self._executions] = AssertionResult( snapshot_location=snapshot_location, snapshot_name=snapshot_name, diff --git a/tests/syrupy/extensions/amber/__snapshots__/test_amber_serializer.ambr b/tests/syrupy/extensions/amber/__snapshots__/test_amber_serializer.ambr index 22cb2fe1..9d2d5417 100644 --- a/tests/syrupy/extensions/amber/__snapshots__/test_amber_serializer.ambr +++ b/tests/syrupy/extensions/amber/__snapshots__/test_amber_serializer.ambr @@ -313,9 +313,15 @@ # name: test_parameter_with_dot[value.with.dot] 'value.with.dot' # --- -# name: test_reflection +# name: test_reflection.1 SnapshotAssertion( - name='snapshot', + name='reflectionA', + num_executions=0, + ) +# --- +# name: test_reflection[reflectionA] + SnapshotAssertion( + name='reflectionA', num_executions=0, ) # --- diff --git a/tests/syrupy/extensions/amber/test_amber_serializer.py b/tests/syrupy/extensions/amber/test_amber_serializer.py index 7066e86b..2385b13b 100644 --- a/tests/syrupy/extensions/amber/test_amber_serializer.py +++ b/tests/syrupy/extensions/amber/test_amber_serializer.py @@ -11,7 +11,10 @@ def test_non_snapshots(snapshot): def test_reflection(snapshot): + """assert [expected|snapshot] == [actual|received]""" + assert snapshot(name="reflectionA") == snapshot assert snapshot == snapshot + assert snapshot == snapshot(name="reflectionA") def test_empty_snapshot(snapshot): diff --git a/tests/syrupy/extensions/json/__snapshots__/test_json_serializer/test_reflection.json b/tests/syrupy/extensions/json/__snapshots__/test_json_serializer/test_reflection.json index 5ccf7701..ee2ed0ca 100644 --- a/tests/syrupy/extensions/json/__snapshots__/test_json_serializer/test_reflection.json +++ b/tests/syrupy/extensions/json/__snapshots__/test_json_serializer/test_reflection.json @@ -1 +1,4 @@ -"" +{ + "name": "snapshot", + "num_executions": 0 +}