Skip to content

Commit 868164a

Browse files
Noahiamogbz
andauthored
fix: yaml multiline string should be read successfully (#62)
* fix: yaml multiline string should be read successfully * wip: more string tests * fix: support empty snapshot * refactor: protected not private method * revert: 0331796 oops meant to do the opposite Co-authored-by: Emmanuel Ogbizi <[email protected]>
1 parent a15276c commit 868164a

File tree

7 files changed

+78
-60
lines changed

7 files changed

+78
-60
lines changed

src/syrupy/serializers/base.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,13 @@ def create_or_update_snapshot(self, data: "SerializableData", index: int) -> Non
8888
self.write(data, index=index)
8989
self.post_write(data, index=index)
9090

91-
@final
91+
@abstractmethod
9292
def delete_snapshot_from_file(self, snapshot_file: str, snapshot_name: str) -> None:
9393
"""
94-
Utility method for removing a snapshot from a snapshot file.
94+
Remove a snapshot from a snapshot file.
95+
If the snapshot file will be empty remove the entire file.
9596
"""
96-
self._write_snapshot_or_remove_file(snapshot_file, snapshot_name, None)
97+
raise NotImplementedError
9798

9899
def pre_read(self, index: int = 0) -> None:
99100
pass
@@ -119,7 +120,7 @@ def pre_write(self, data: "SerializableData", index: int = 0) -> None:
119120
@final
120121
def write(self, data: "SerializableData", index: int = 0) -> None:
121122
"""
122-
Override `_write_snapshot_or_remove_file` in subclass to change behaviour
123+
Override `_write_snapshot_to_file` in subclass to change behaviour
123124
"""
124125
snapshot_file = self.get_filepath(index)
125126
snapshot_name = self.get_snapshot_name(index)
@@ -129,7 +130,7 @@ def write(self, data: "SerializableData", index: int = 0) -> None:
129130
f" '{self.test_location.testname}'"
130131
)
131132
warnings.warn(warning_msg)
132-
self._write_snapshot_or_remove_file(snapshot_file, snapshot_name, data)
133+
self._write_snapshot_to_file(snapshot_file, snapshot_name, data)
133134

134135
def post_write(self, data: "SerializableData", index: int = 0) -> None:
135136
pass
@@ -170,12 +171,10 @@ def _read_snapshot_from_file(
170171
raise NotImplementedError
171172

172173
@abstractmethod
173-
def _write_snapshot_or_remove_file(
174+
def _write_snapshot_to_file(
174175
self, snapshot_file: str, snapshot_name: str, data: "SerializableData"
175176
) -> None:
176177
"""
177178
Adds the snapshot data to the snapshots read from the file
178-
or removes the snapshot entry if data is `None`.
179-
If the snapshot file will be empty remove the entire file.
180179
"""
181180
raise NotImplementedError

src/syrupy/serializers/raw_single.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ def _read_file(self, filepath: str) -> Any:
4444
except FileNotFoundError:
4545
return None
4646

47-
def _write_snapshot_or_remove_file(
47+
def _write_snapshot_to_file(
4848
self, snapshot_file: str, snapshot_name: str, data: "SerializableData"
4949
) -> None:
50-
if data:
51-
self.__write_file(snapshot_file, data)
52-
else:
53-
os.remove(snapshot_file)
50+
self.__write_file(snapshot_file, data)
51+
52+
def delete_snapshot_from_file(self, snapshot_file: str, snapshot_name: str) -> None:
53+
os.remove(snapshot_file)
5454

5555
def __write_file(self, filepath: str, data: "SerializableData") -> None:
5656
with open(filepath, "wb") as f:

src/syrupy/serializers/yaml.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,18 @@ def _read_snapshot_from_file(
2929
raw_snapshots = self._read_raw_file(snapshot_file)
3030
return raw_snapshots.get(snapshot_name, None)
3131

32-
def _write_snapshot_or_remove_file(
32+
def _write_snapshot_to_file(
3333
self, snapshot_file: str, snapshot_name: str, data: "SerializableData"
3434
) -> None:
35-
"""
36-
Adds the snapshot data to the snapshots read from the file
37-
or removes the snapshot entry if data is `None`.
38-
If the snapshot file will be empty remove the entire file.
39-
"""
40-
snapshots = self._read_file(snapshot_file)
41-
if data is None and snapshot_name in snapshots:
35+
snapshots = self.__read_file(snapshot_file)
36+
snapshots[snapshot_name] = snapshots.get(snapshot_name, {})
37+
snapshots[snapshot_name][self._data_key] = data
38+
self.__write_file(snapshot_file, snapshots)
39+
40+
def delete_snapshot_from_file(self, snapshot_file: str, snapshot_name: str) -> None:
41+
snapshots = self.__read_file(snapshot_file)
42+
if snapshot_name in snapshots:
4243
del snapshots[snapshot_name]
43-
else:
44-
snapshots[snapshot_name] = snapshots.get(snapshot_name, {})
45-
snapshots[snapshot_name][self._data_key] = data
4644

4745
if snapshots:
4846
self.__write_file(snapshot_file, snapshots)
@@ -67,7 +65,7 @@ def __write_file(self, filepath: str, data: "SerializableData") -> None:
6765
with open(filepath, "w") as f:
6866
yaml.dump(data, f, allow_unicode=True)
6967

70-
def _read_file(self, filepath: str) -> Any:
68+
def __read_file(self, filepath: str) -> Any:
7169
"""
7270
Read the snapshot data from the snapshot file into a python instance.
7371
"""
@@ -89,12 +87,12 @@ def _read_raw_file(self, filepath: str) -> Dict[str, str]:
8987
with open(filepath, "r") as f:
9088
test_name = None
9189
for line in f:
92-
indent = len(line) - len(line.lstrip(" "))
93-
if not indent:
90+
if line[0] not in (" ", "\n") and line[-2] == ":":
9491
test_name = line[:-2] # newline & colon
9592
snapshots[test_name] = ""
9693
elif test_name is not None:
97-
snapshots[test_name] += line[2:]
94+
offset = min(len(line) - 1, 2)
95+
snapshots[test_name] += line[offset:]
9896
except FileNotFoundError:
9997
pass
10098

src/syrupy/session.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,6 @@ def remove_unused_snapshots(
142142
self._serializers[snapshot_file].delete_snapshot_from_file(
143143
snapshot_file, snapshot_name
144144
)
145-
if (
146-
os.path.exists(snapshot_file)
147-
and snapshot_file not in used_snapshot_files
148-
):
149-
os.remove(snapshot_file)
150145

151146
def _collate_snapshots(self) -> None:
152147
"""

tests/__snapshots__/test_snapshots.yaml

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,40 +18,56 @@ test_dict[actual1]:
1818
d:
1919
- '1'
2020
- 2
21+
test_empty_snapshot:
22+
data: null
23+
test_empty_snapshot.1:
24+
data: ''
2125
test_multiple_snapshots:
2226
data: First.
2327
test_multiple_snapshots.1:
2428
data: Second.
2529
test_multiple_snapshots.2:
2630
data: Third.
27-
test_parametrized_with_special_char[Backslash \\u U]:
28-
data: Backslash \u U
29-
test_parametrized_with_special_char[Escaped \\n]:
30-
data: Escaped \n
31-
test_raw_string:
32-
data: Raw string
3331
test_set:
3432
data: !!set
3533
a: null
3634
is: null
3735
set: null
3836
this: null
39-
test_simple_string:
40-
data: Loreeeeeem ipsum.
41-
test_tuples:
37+
test_string[0]:
38+
data: ''
39+
test_string[1]:
40+
data: Raw string
41+
test_string[2]:
42+
data: Escaped \n
43+
test_string[3]:
44+
data: Backslash \u U
45+
test_string[4]:
46+
data: 🥞🐍🍯
47+
test_string[5]:
48+
data: 'singleline:'
49+
test_string[6]:
50+
data: '- singleline'
51+
test_string[7]:
52+
data: 'multi-line
53+
54+
line 2
55+
56+
line 3'
57+
test_string[8]:
58+
data: "multi-line\nline 2\n line 3"
59+
test_tuple:
4260
data: !!python/tuple
4361
- this
4462
- is
4563
- !!python/tuple
4664
- a
4765
- tuple
48-
test_tuples.1:
66+
test_tuple.1:
4967
data: !!python/object/new:tests.test_snapshots.ExampleTuple
5068
- this
5169
- is
5270
- a
5371
- !!set
5472
named: null
5573
tuple: null
56-
test_unicode_string:
57-
data: 🥞🐍🍯

tests/test_plugin_custom.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ def get_snapshot_name(self, index = 0):
2828
def _read_snapshot_from_file(self, file, name):
2929
pass
3030
31-
def _write_snapshot_or_remove_file(self, file, name, data):
31+
def _write_snapshot_to_file(self, file, name, data):
32+
pass
33+
34+
def delete_snapshot_from_file(self, file, name):
3235
pass
3336
3437

tests/test_snapshots.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,28 @@ def test_non_snapshots(snapshot):
88
assert "Lorem ipsum." == "Muspi merol."
99

1010

11-
def test_simple_string(snapshot):
12-
assert "Loreeeeeem ipsum." == snapshot
11+
def test_empty_snapshot(snapshot):
12+
assert snapshot == None # noqa: E711
13+
assert snapshot == ""
1314

1415

15-
def test_raw_string(snapshot):
16-
assert r"Raw string" == snapshot
17-
18-
19-
def test_unicode_string(snapshot):
20-
assert "🥞🐍🍯" == snapshot
16+
@pytest.mark.parametrize(
17+
"actual",
18+
[
19+
"",
20+
r"Raw string",
21+
r"Escaped \n",
22+
r"Backslash \u U",
23+
"🥞🐍🍯",
24+
"singleline:",
25+
"- singleline",
26+
"multi-line\nline 2\nline 3",
27+
"multi-line\nline 2\n line 3",
28+
],
29+
ids=lambda x: "",
30+
)
31+
def test_string(snapshot, actual):
32+
assert snapshot == actual
2133

2234

2335
def test_multiple_snapshots(snapshot):
@@ -26,11 +38,6 @@ def test_multiple_snapshots(snapshot):
2638
snapshot("Third.")
2739

2840

29-
@pytest.mark.parametrize("expected", [r"Escaped \n", r"Backslash \u U"])
30-
def test_parametrized_with_special_char(snapshot, expected):
31-
assert expected == snapshot
32-
33-
3441
@pytest.mark.parametrize(
3542
"actual",
3643
[
@@ -49,7 +56,7 @@ def test_set(snapshot):
4956
ExampleTuple = namedtuple("ExampleTuple", ["a", "b", "c", "d"])
5057

5158

52-
def test_tuples(snapshot):
59+
def test_tuple(snapshot):
5360
assert snapshot == ("this", "is", ("a", "tuple"))
5461
assert snapshot == ExampleTuple(a="this", b="is", c="a", d={"named", "tuple"})
5562

0 commit comments

Comments
 (0)