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

feat(): add assert_match_with_ignore #161

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions snapshottest/ignore.py
Original file line number Diff line number Diff line change
@@ -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
56 changes: 56 additions & 0 deletions snapshottest/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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()

Expand Down
27 changes: 27 additions & 0 deletions tests/test_ignore.py
Original file line number Diff line number Diff line change
@@ -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
75 changes: 74 additions & 1 deletion tests/test_snapshot_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from collections import OrderedDict

from time import time
from snapshottest.module import SnapshotModule, SnapshotTest


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