From 2d36aceec894196ae52f75223b40bfcc71e291b8 Mon Sep 17 00:00:00 2001 From: LuisFros Date: Fri, 7 May 2021 15:30:24 +0200 Subject: [PATCH] feat(): add assert_with_ignore fix(): add compatibility with PY 3.5 --- snapshottest/ignore.py | 42 +++++++++++++++++++++ snapshottest/module.py | 56 +++++++++++++++++++++++++++ tests/test_ignore.py | 27 +++++++++++++ tests/test_snapshot_test.py | 75 ++++++++++++++++++++++++++++++++++++- 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 snapshottest/ignore.py create mode 100644 tests/test_ignore.py diff --git a/snapshottest/ignore.py b/snapshottest/ignore.py new file mode 100644 index 0000000..58e06fe --- /dev/null +++ b/snapshottest/ignore.py @@ -0,0 +1,42 @@ +import re + + +def update_path(current_path, key_or_index, is_dict=False): + if is_dict: + if current_path == "": + return key_or_index + return "{}.{}".format(current_path, key_or_index) + else: + return "{}[{}]".format(current_path, key_or_index) + + +def clear_ignore_keys(data, ignore_keys, current_path=""): + if isinstance(data, dict): + for key, value in data.items(): + temp_path = update_path(current_path, key, is_dict=True) + matched = match_ignored_key(key, data, ignore_keys, temp_path) + if not matched: + data[key] = clear_ignore_keys(value, ignore_keys, temp_path) + return data + elif isinstance(data, list): + for index, value in enumerate(data): + temp_path = update_path(current_path, index) + matched = match_ignored_key(index, data, ignore_keys, temp_path) + if not matched: + data[index] = clear_ignore_keys(value, ignore_keys, temp_path) + return data + elif isinstance(data, tuple): + return tuple( + clear_ignore_keys(value, ignore_keys, update_path(current_path, index)) + for index, value in enumerate(data) + ) + return data + + +def match_ignored_key(key_or_index, data, ignore_keys, temp_path): + for ignored in ignore_keys: + escaped = ignored.translate(str.maketrans({"[": r"\[", "]": r"\]"})) + if re.match("{}$".format(escaped), temp_path): + data[key_or_index] = None + return True + return False diff --git a/snapshottest/module.py b/snapshottest/module.py index e31b46c..c763ce0 100644 --- a/snapshottest/module.py +++ b/snapshottest/module.py @@ -8,6 +8,7 @@ from .snapshot import Snapshot from .formatter import Formatter from .error import SnapshotNotFound +from .ignore import clear_ignore_keys logger = logging.getLogger(__name__) @@ -266,6 +267,61 @@ def assert_match(self, value, name=""): if not name: self.snapshot_counter += 1 + def assert_match_with_ignore(self, data, ignore_keys): + """Extension of assert_match to ignore data. + Args: + data (dict,list,tuple): Data to be asserted + ignored_keys (list): List of strings containing path to be ignored, + special character "[.]" can be used to ignore multiple elements in list. + (See Example 2) + Returns: + None: Asserts if the values are the same + Raises: + AssertionError: If the snapshot is different than the incoming data + Examples: + Test examples at: apps/tests/utils/test_asserts.py + Example 1: + >>> data={"dict1": {"dict2": {"dict3": {"id": "importantId"} } } } + >>> ignore_keys=["dict1.dict2.dict3.id"] + >>> assert_match_with_ignore(data,ignore_keys) + # Will create the following snapshot + snapshots['example_snapshot'] = { + 'dict1': { + 'dict2': { + 'dict3': { + 'id': None, + 'other': 'value' + } + } + } + } + --- + Example 2: + >>> data=[ + { + "name": "objectList", + "children": [ + {"id": "random_string", "name": "child_1",}, + {"id": "random_string2", "name": "child_2",}, + ], + } + ] + >>> ignore_keys=["[0].children[.].id"] + >>> assert_match_with_ignore(data,ignore_keys) + # Will create the following snapshot + snapshots['example2_snapshot'] = [ + { + "name": "objectList", + "children": [ + {"id": None, "name": "child_1",}, + {"id": None, "name": "child_2",}, + ], + } + ] + """ + + self.assert_match(clear_ignore_keys(data, ignore_keys)) + def save_changes(self): self.module.save() diff --git a/tests/test_ignore.py b/tests/test_ignore.py new file mode 100644 index 0000000..2b27d04 --- /dev/null +++ b/tests/test_ignore.py @@ -0,0 +1,27 @@ +from time import time +from snapshottest.ignore import clear_ignore_keys + +DATA = { + "name": { + "id": time(), + "first": "Manual", + "last": "gonazales", + "cities": ["1", "2", {"id": time()}], + } +} + +DATA_EXPECTED = { + "name": { + "id": None, + "first": "Manual", + "last": "gonazales", + "cities": [None, "2", {"id": None}], + } +} + + +def test_clear_works(): + clean_data = clear_ignore_keys( + DATA, ignore_keys=["name.id", "name.cities[0]", "name.cities[2].id"] + ) + assert clean_data == DATA_EXPECTED diff --git a/tests/test_snapshot_test.py b/tests/test_snapshot_test.py index 9084f87..5883c81 100644 --- a/tests/test_snapshot_test.py +++ b/tests/test_snapshot_test.py @@ -1,6 +1,6 @@ import pytest from collections import OrderedDict - +from time import time from snapshottest.module import SnapshotModule, SnapshotTest @@ -121,3 +121,76 @@ def test_snapshot_does_not_match_other_values(snapshot_test, value, other_value) with pytest.raises(AssertionError): snapshot_test.assert_match(other_value) assert_snapshot_test_failed(snapshot_test) + + +SNAPSHOTABLE_VALUES_WITH_IGNORE = [ + { + "data": {"dict1": {"dict2": {"dict3": {"id": time(), "other": "value"}}}}, + "ignore_keys": ["dict1.dict2.dict3.id"], + }, + { + "data": [ + { + "A": { + "id": time(), + "B": 1, + "C": 2, + "D": [0, 1, {"A": 1}], + } + }, + { + "A": { + "id": time(), + "B": 2, + "C": 3, + "D": [0, 1, {"id": time()}], + } + }, + ], + "ignore_keys": ["[.].A.id", "[1].A.C[.].id"], + }, + { + "data": { + "A": { + "id": [0, 1, 2, 3, time()], + "A": 1, + "B": 2, + "C": [ + {"id": time()}, + {"id": time(), "A": 0}, + {"id": time()}, + ], + } + }, + "ignore_keys": ["A.C[.].id", "A.id[4]"], + }, + { + "data": { + "A": { + "A": { + "id": time(), + "A": 1, + "B": 2, + "C": 3, + "D": [ + {"id": [0, time()]}, + {"id": {"A": [time()]}, "B": 0}, + {"id": time()}, + ], + } + } + }, + "ignore_keys": ["A.A.id", "A.A.D.id[1]", "A.A.D.id.A[0"], + }, +] + + +@pytest.mark.parametrize("values", SNAPSHOTABLE_VALUES_WITH_IGNORE, ids=repr) +def test_snapshot_with_ignore(snapshot_test, values): + data = values["data"] + ignore_keys = values["ignore_keys"] + snapshot_test.assert_match_with_ignore(data, ignore_keys) + + snapshot_test.reinitialize() + snapshot_test.assert_match_with_ignore(data, ignore_keys) + assert_snapshot_test_succeeded(snapshot_test)