Skip to content

Commit 27b09fd

Browse files
tiberlas=
authored andcommitted
test: add tests for footnote support
1 parent 532f7bf commit 27b09fd

File tree

3 files changed

+96
-0
lines changed

3 files changed

+96
-0
lines changed

tests/test_document.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from docx.table import Table
2424
from docx.text.paragraph import Paragraph
2525
from docx.text.run import Run
26+
from docx.footnotes import Footnotes
2627

2728
from .unitutil.cxml import element, xml
2829
from .unitutil.mock import Mock, class_mock, instance_mock, method_mock, property_mock
@@ -103,6 +104,10 @@ def it_provides_access_to_its_core_properties(self, core_props_fixture):
103104
core_properties = document.core_properties
104105
assert core_properties is core_properties_
105106

107+
def it_provides_access_to_its_footnotes(self, footnotes_fixture):
108+
document, footnotes_ = footnotes_fixture
109+
assert document.footnotes is footnotes_
110+
106111
def it_provides_access_to_its_inline_shapes(self, inline_shapes_fixture):
107112
document, inline_shapes_ = inline_shapes_fixture
108113
assert document.inline_shapes is inline_shapes_
@@ -246,6 +251,18 @@ def core_props_fixture(self, document_part_, core_properties_):
246251
document_part_.core_properties = core_properties_
247252
return document, core_properties_
248253

254+
@pytest.fixture(params=[
255+
('w:footnotes/(w:footnote{w:id=-1}/w:p/w:r/w:t"minus one note", w:footnote{w:id=0}/w:p/w:r/w:t"zero note")'),
256+
('w:footnotes/(w:footnote{w:id=1}/w:p/w:r/w:t"first note", w:footnote{w:id=2}/w:p/w:r/w:t"second note")'),
257+
('w:footnotes/(w:footnote{w:id=1}/w:p/w:r/w:t"first note", w:footnote{w:id=2}/w:p/w:r/w:t"second note", w:footnote{w:id=3}/w:p/w:r/w:t"third note")'),
258+
])
259+
def footnotes_fixture(self, request, document_part_):
260+
footnotes_cxml = request.param
261+
document = Document(None, document_part_)
262+
footnotes = Footnotes(element(footnotes_cxml), None)
263+
document_part_.footnotes = footnotes
264+
return document, footnotes
265+
249266
@pytest.fixture
250267
def inline_shapes_fixture(self, document_part_, inline_shapes_):
251268
document = Document(None, document_part_)

tests/test_section.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,63 @@ def it_can_change_whether_the_document_has_distinct_odd_and_even_headers(
161161

162162
assert sectPr.xml == expected_xml
163163

164+
@pytest.mark.parametrize(
165+
("sectPr_cxml", "footnote_prop_name", "expected_value"),
166+
[
167+
("w:sectPr/w:footnotePr/w:numFmt{w:val=decimal}", "footnote_number_format", "decimal"),
168+
("w:sectPr/w:footnotePr/w:numFmt{w:val=upperRoman}", "footnote_number_format", "upperRoman"),
169+
("w:sectPr/w:footnotePr/w:numFmt{w:val=lowerLetter}", "footnote_number_format", "lowerLetter"),
170+
("w:sectPr/w:footnotePr/w:numFmt{w:val=bullet}", "footnote_number_format", "bullet"),
171+
("w:sectPr/w:footnotePr/w:pos{w:val=pageBottom}", "footnote_number_format", None),
172+
("w:sectPr/w:footnotePr/w:pos{w:val=pageBottom}", "footnote_position", "pageBottom"),
173+
("w:sectPr/w:footnotePr/w:numStart{w:val=5}", "footnote_numbering_start_value", 5),
174+
("w:sectPr/w:footnotePr/w:numStart{w:val=13}", "footnote_numbering_start_value", 13),
175+
("w:sectPr/w:footnotePr/w:numRestart{w:val=eachSect}", "footnote_numbering_restart_location", "eachSect"),
176+
("w:sectPr/w:footnotePr/w:numRestart{w:val=eachPage}", "footnote_numbering_restart_location", "eachPage"),
177+
],
178+
)
179+
def it_knows_its_footnote_properties(
180+
self,
181+
sectPr_cxml: str,
182+
footnote_prop_name: str,
183+
expected_value: str | int | None,
184+
document_part_: Mock,
185+
):
186+
sectPr = cast(CT_SectPr, element(sectPr_cxml))
187+
section = Section(sectPr, document_part_)
188+
189+
value = getattr(section, footnote_prop_name)
190+
191+
assert value == expected_value
192+
193+
@pytest.mark.parametrize(
194+
("sectPr_cxml", "footnote_prop_name", "value", "expected_cxml"),
195+
[
196+
("w:sectPr", "footnote_number_format", "upperRoman", "w:sectPr/w:footnotePr/w:numFmt{w:val=upperRoman}"),
197+
("w:sectPr/w:footnotePr/w:numFmt{w:val=decimal}", "footnote_number_format", "upperRoman", "w:sectPr/w:footnotePr/w:numFmt{w:val=upperRoman}"),
198+
("w:sectPr", "footnote_position", "pageBottom", "w:sectPr/w:footnotePr/w:pos{w:val=pageBottom}"),
199+
("w:sectPr", "footnote_numbering_start_value", 1, "w:sectPr/w:footnotePr/(w:numStart{w:val=1},w:numRestart{w:val=continuous})"),
200+
("w:sectPr", "footnote_numbering_start_value", 5, "w:sectPr/w:footnotePr/(w:numStart{w:val=5},w:numRestart{w:val=continuous})"),
201+
("w:sectPr", "footnote_numbering_restart_location", "eachSect", "w:sectPr/w:footnotePr/(w:numStart{w:val=1},w:numRestart{w:val=eachSect})"),
202+
("w:sectPr", "footnote_numbering_restart_location", "continuous", "w:sectPr/w:footnotePr/(w:numStart{w:val=1},w:numRestart{w:val=continuous})"),
203+
],
204+
)
205+
def it_can_change_its_footnote_properties(
206+
self,
207+
sectPr_cxml: str,
208+
footnote_prop_name: str,
209+
value: str | int | None,
210+
expected_cxml: str,
211+
document_part_: Mock,
212+
):
213+
sectPr = cast(CT_SectPr, element(sectPr_cxml))
214+
expected_xml = xml(expected_cxml)
215+
section = Section(sectPr, document_part_)
216+
217+
setattr(section, footnote_prop_name, value)
218+
219+
assert section._sectPr.xml == expected_xml
220+
164221
def it_provides_access_to_its_even_page_footer(
165222
self, document_part_: Mock, _Footer_: Mock, footer_: Mock
166223
):

tests/text/test_paragraph.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from docx.text.paragraph import Paragraph
1414
from docx.text.parfmt import ParagraphFormat
1515
from docx.text.run import Run
16+
from docx.footnotes import Footnotes
17+
from docx.document import Document
1618

1719
from ..unitutil.cxml import element, xml
1820
from ..unitutil.mock import call, class_mock, instance_mock, method_mock, property_mock
@@ -38,6 +40,10 @@ def it_knows_whether_it_contains_a_page_break(
3840

3941
assert paragraph.contains_page_break == expected_value
4042

43+
def it_provides_access_to_its_footnotes(self, footnotes_fixture):
44+
paragraph, footnotes_ = footnotes_fixture
45+
assert paragraph.footnotes == footnotes_
46+
4147
@pytest.mark.parametrize(
4248
("p_cxml", "count"),
4349
[
@@ -215,6 +221,22 @@ def it_inserts_a_paragraph_before_to_help(self, _insert_before_fixture):
215221

216222
# fixtures -------------------------------------------------------
217223

224+
@pytest.fixture(params=[
225+
('w:p/w:r/w:footnoteReference{w:id=2}', 'w:footnotes/(w:footnote{w:id=1}/w:p/w:r/w:t"first note", w:footnote{w:id=2}/w:p/w:r/w:t"second note")', [2]),
226+
('w:p/w:r/w:footnoteReference{w:id=1}', 'w:footnotes/(w:footnote{w:id=1}/w:p/w:r/w:t"first note", w:footnote{w:id=2}/w:p/w:r/w:t"second note")', [1]),
227+
('w:p/w:r/(w:footnoteReference{w:id=1}, w:footnoteReference{w:id=2})', 'w:footnotes/(w:footnote{w:id=1}/w:p/w:r/w:t"first note", w:footnote{w:id=2}/w:p/w:r/w:t"second note")', [1,2]),
228+
('w:p/w:r/(w:footnoteReference{w:id=3}, w:footnoteReference{w:id=2})', 'w:footnotes/(w:footnote{w:id=1}/w:p/w:r/w:t"first note", w:footnote{w:id=2}/w:p/w:r/w:t"second note", w:footnote{w:id=3}/w:p/w:r/w:t"third note")', [3,2]),
229+
])
230+
def footnotes_fixture(self, request, document_part_):
231+
paragraph_cxml, footnotes_cxml, footnote_ids_in_p = request.param
232+
document_elm = element('w:document/w:body')
233+
document = Document(document_elm, document_part_)
234+
paragraph = Paragraph(element(paragraph_cxml), document._body)
235+
footnotes = Footnotes(element(footnotes_cxml), None)
236+
footnotes_in_p = [footnotes[id] for id in footnote_ids_in_p]
237+
document_part_.footnotes = footnotes
238+
return paragraph, footnotes_in_p
239+
218240
@pytest.fixture(
219241
params=[
220242
("w:p", None, None, "w:p/w:r"),

0 commit comments

Comments
 (0)