Skip to content

Commit 8297f2c

Browse files
author
Steve Canny
committed
txt: refactor Paragraph.insert_paragraph_before()
Extract _insert_paragraph_before() helper method. The helper method can be tested integrated with oxml. The remaining part of the method is tested with mocks. Aside from being good hygiene, this removes the integration dependency on paragraph.style, allowing that property to be reimplemented without causing a fail in the _insert_paragraph_before unit tests.
1 parent 06ead3c commit 8297f2c

2 files changed

Lines changed: 53 additions & 14 deletions

File tree

docx/text/paragraph.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ def insert_paragraph_before(self, text=None, style=None):
6565
text in a single run. If *style* is provided, that style is assigned
6666
to the new paragraph.
6767
"""
68-
p = self._p.add_p_before()
69-
paragraph = Paragraph(p, self._parent)
68+
paragraph = self._insert_paragraph_before()
7069
if text:
7170
paragraph.add_run(text)
7271
if style is not None:
@@ -116,3 +115,11 @@ def text(self):
116115
def text(self, text):
117116
self.clear()
118117
self.add_run(text)
118+
119+
def _insert_paragraph_before(self):
120+
"""
121+
Return a newly created paragraph, inserted directly before this
122+
paragraph.
123+
"""
124+
p = self._p.add_p_before()
125+
return Paragraph(p, self._parent)

tests/text/test_paragraph.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
)
1010

1111
from docx.enum.text import WD_ALIGN_PARAGRAPH
12-
from docx.oxml.ns import qn
1312
from docx.oxml.text.paragraph import CT_P
1413
from docx.oxml.text.run import CT_R
1514
from docx.text.paragraph import Paragraph
@@ -18,7 +17,7 @@
1817
import pytest
1918

2019
from ..unitutil.cxml import element, xml
21-
from ..unitutil.mock import call, class_mock, instance_mock
20+
from ..unitutil.mock import call, class_mock, instance_mock, method_mock
2221

2322

2423
class DescribeParagraph(object):
@@ -66,12 +65,15 @@ def it_can_replace_the_text_it_contains(self, text_set_fixture):
6665
assert paragraph.text == expected_text
6766

6867
def it_can_insert_a_paragraph_before_itself(self, insert_before_fixture):
69-
paragraph, text, style, body, expected_xml = insert_before_fixture
68+
paragraph, text, style, paragraph_, add_run_calls = (
69+
insert_before_fixture
70+
)
7071
new_paragraph = paragraph.insert_paragraph_before(text, style)
71-
assert isinstance(new_paragraph, Paragraph)
72-
assert new_paragraph.text == text
72+
73+
paragraph._insert_paragraph_before.assert_called_once_with()
74+
assert new_paragraph.add_run.call_args_list == add_run_calls
7375
assert new_paragraph.style == style
74-
assert body.xml == expected_xml
76+
assert new_paragraph is paragraph_
7577

7678
def it_can_remove_its_content_while_preserving_formatting(
7779
self, clear_fixture):
@@ -80,6 +82,12 @@ def it_can_remove_its_content_while_preserving_formatting(
8082
assert paragraph._p.xml == expected_xml
8183
assert _paragraph is paragraph
8284

85+
def it_inserts_a_paragraph_before_to_help(self, _insert_before_fixture):
86+
paragraph, body, expected_xml = _insert_before_fixture
87+
new_paragraph = paragraph._insert_paragraph_before()
88+
assert isinstance(new_paragraph, Paragraph)
89+
assert body.xml == expected_xml
90+
8391
# fixtures -------------------------------------------------------
8492

8593
@pytest.fixture(params=[
@@ -135,15 +143,31 @@ def clear_fixture(self, request):
135143
return paragraph, expected_xml
136144

137145
@pytest.fixture(params=[
138-
('w:body/w:p', 'foobar', 'Heading1',
139-
'w:body/(w:p/(w:pPr/w:pStyle{w:val=Heading1},w:r/w:t"foobar"),w:p)')
146+
(None, None),
147+
('Foo', None),
148+
(None, 'Bar'),
149+
('Foo', 'Bar'),
140150
])
141-
def insert_before_fixture(self, request):
142-
body_cxml, text, style, expected_cxml = request.param
151+
def insert_before_fixture(self, request, _insert_paragraph_before_,
152+
add_run_):
153+
paragraph = Paragraph(None, None)
154+
paragraph_ = _insert_paragraph_before_.return_value
155+
text, style = request.param
156+
add_run_calls = [] if text is None else [call(text)]
157+
paragraph_.style = None
158+
return (
159+
paragraph, text, style, paragraph_, add_run_calls
160+
)
161+
162+
@pytest.fixture(params=[
163+
('w:body/w:p{id=42}', 'w:body/(w:p,w:p{id=42})')
164+
])
165+
def _insert_before_fixture(self, request):
166+
body_cxml, expected_cxml = request.param
143167
body = element(body_cxml)
144-
paragraph = Paragraph(body.find(qn('w:p')), None)
168+
paragraph = Paragraph(body[0], None)
145169
expected_xml = xml(expected_cxml)
146-
return paragraph, text, style, body, expected_xml
170+
return paragraph, body, expected_xml
147171

148172
@pytest.fixture
149173
def runs_fixture(self, p_, Run_, r_, r_2_, runs_):
@@ -205,6 +229,14 @@ def text_set_fixture(self):
205229

206230
# fixture components ---------------------------------------------
207231

232+
@pytest.fixture
233+
def add_run_(self, request):
234+
return method_mock(request, Paragraph, 'add_run')
235+
236+
@pytest.fixture
237+
def _insert_paragraph_before_(self, request):
238+
return method_mock(request, Paragraph, '_insert_paragraph_before')
239+
208240
@pytest.fixture
209241
def p_(self, request, r_, r_2_):
210242
return instance_mock(request, CT_P, r_lst=(r_, r_2_))

0 commit comments

Comments
 (0)