Skip to content

Commit 532f7bf

Browse files
tiberlas=
authored andcommitted
acpt: add scenario for footnotes
1 parent d608320 commit 532f7bf

File tree

6 files changed

+240
-0
lines changed

6 files changed

+240
-0
lines changed

features/doc-access-footnotes.feature

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Feature: Access document footnotes
2+
In order to operate on an individual footnote
3+
As a developer using python-docx
4+
I need access to each footnote in the footnote collection of a document
5+
I need access to footnote properties
6+
7+
Scenario: Access footnote from a document containing footnotes
8+
Given a document with 3 footnotes and 2 default footnotes
9+
Then len(footnotes) is 5
10+
And I can access a footnote by footnote reference id
11+
And I can access a paragraph in a specific footnote
12+
13+
Scenario: Access a footnote from document with an invalid footnote reference id
14+
Given a document with footnotes
15+
When I try to access a footnote with invalid reference id
16+
Then it trows an IndexError
17+
18+
Scenario Outline: Access footnote properties
19+
Given a document with footnotes and with all footnotes properties
20+
Then I can access footnote property <propName> with value <value>
21+
22+
Examples: footnote property names and values
23+
| propName | value |
24+
| footnote_position | str('pageBottom') |
25+
| footnote_number_format | str('lowerRoman') |
26+
| footnote_numbering_start_value | int(1) |
27+
| footnote_numbering_restart_location | str('continuous') |
28+
29+
Scenario Outline: Access footnotes and footnote properties in a document without footnotes
30+
Given a document without footnotes
31+
# there are always 2 default footnotes with footnote reference id of -1 and 0
32+
Then len(footnotes) is 2
33+
And I can access footnote property <propName> with value <value>
34+
35+
Examples: footnote property names and values
36+
| propName | value |
37+
| footnote_position | None |
38+
| footnote_number_format | None |
39+
| footnote_numbering_start_value | None |
40+
| footnote_numbering_restart_location | None |
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Feature: Set footnote properties
2+
In order to change footnote properties of a document
3+
As a developer using python-docx
4+
I need a setter for footnote properties
5+
6+
Scenario Outline: Change footnote properties
7+
Given a document with footnotes and with all footnotes properties
8+
When I change footnote property <propName> to <value>
9+
Then I can access footnote property <propName> with value <value>
10+
11+
Examples: footnote property names and values
12+
| propName | value |
13+
| footnote_position | str('beneathText') |
14+
| footnote_position | str('pageBottom') |
15+
| footnote_number_format | str('upperRoman') |
16+
| footnote_number_format | str('decimal') |
17+
| footnote_number_format | str('hex') |
18+
| footnote_numbering_start_value | int(10) |
19+
| footnote_numbering_restart_location | str('eachPage') |
20+
| footnote_numbering_restart_location | str('eachSect') |
21+
| footnote_numbering_restart_location | str('continuous') |

features/par-access-footnotes.feature

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Feature: Access paragraph footnotes
2+
In order to operate on an individual footnote
3+
As a developer using python-docx
4+
I need access to every footnote if present in s specific paragraph
5+
6+
7+
Scenario Outline: Access all footnote text from a paragraph that might contain a footnote
8+
Given a document with paragraphs[0] containing one, paragraphs[1] containing none, and paragraphs[2] containing two footnotes
9+
Then paragraphs[<parId>] has footnote reference ids of <refIds>, with footnote text <fText>
10+
11+
Examples: footnote values per paragraph
12+
| parId | refIds | fText |
13+
| 0 | int(1) | str(' This is footnote text for the first footnote.') |
14+
| 1 | None | None |
15+
| 2 | [2,3] | [' This is footnote text for the second footnote.', ' This is footnote text for the third footnote.'] |

features/par-insert-footnote.feature

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Feature: Insert a footnote at the end of a paragraph
2+
In order to add new footnote at the end of a text (paragraph)
3+
As a developer using python-docx
4+
I need a way to add a footnote to the end of a specific paragraph
5+
6+
7+
Scenario: Add a new footnote to a paragraph in a document without footnotes
8+
Given a paragraph in a document without footnotes
9+
When I add a footnote to the paragraphs[1] with text ' NEW FOOTNOTE'
10+
Then the document contains a footnote with footnote reference id of 1 with text ' NEW FOOTNOTE'
11+
And len(footnotes) is 3
12+
13+
Scenario Outline: Add a new footnote to a paragraph in a document containing one footnote before the paragraph and two footnote after
14+
Given a document with paragraphs[0] containing one, paragraphs[1] containing none, and paragraphs[2] containing two footnotes
15+
When I add a footnote to the paragraphs[1] with text ' NEW FOOTNOTE'
16+
Then paragraphs[<parId>] has footnote reference ids of <refIds>, with footnote text <fText>
17+
And len(footnotes) is 6
18+
19+
Examples: footnote values per paragraph
20+
| parId | refIds | fText |
21+
| 0 | int(1) | str(' This is footnote text for the first footnote.') |
22+
| 1 | int(2) | str(' NEW FOOTNOTE') |
23+
| 2 | [3,4] | [' This is footnote text for the second footnote.',' This is footnote text for the third footnote.'] |

features/steps/footnotes.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
"""Step implementations for footnote-related features."""
2+
3+
from behave import given, when, then
4+
from behave.runner import Context
5+
6+
from docx import Document
7+
from docx.footnotes import Footnote
8+
from docx.text.paragraph import Paragraph
9+
10+
from helpers import test_docx
11+
12+
# given ====================================================
13+
14+
15+
@given("a document with 3 footnotes and 2 default footnotes")
16+
def given_a_document_with_3_footnotes_and_2_default_footnotes(context: Context):
17+
document = Document(test_docx("footnotes"))
18+
context.footnotes = document.footnotes
19+
20+
21+
@given("a document with footnotes and with all footnotes properties")
22+
def given_a_document_with_footnotes_and_with_all_footnotes_properties(context: Context):
23+
document = Document(test_docx("footnotes"))
24+
context.section = document.sections[0]
25+
26+
27+
@given("a document with footnotes")
28+
def given_a_document_with_footnotes(context: Context):
29+
document = Document(test_docx("footnotes"))
30+
context.footnotes = document.footnotes
31+
32+
33+
@given("a document without footnotes")
34+
def given_a_document_without_footnotes(context: Context):
35+
document = Document(test_docx("doc-default"))
36+
context.footnotes = document.footnotes
37+
context.section = document.sections[0]
38+
39+
40+
@given("a paragraph in a document without footnotes")
41+
def given_a_paragraph_in_a_document_without_footnotes(context: Context):
42+
document = Document(test_docx("par-known-paragraphs"))
43+
context.paragraphs = document.paragraphs
44+
context.footnotes = document.footnotes
45+
46+
47+
@given("a document with paragraphs[0] containing one, paragraphs[1] containing none, and paragraphs[2] containing two footnotes")
48+
def given_a_document_with_3_footnotes(context: Context):
49+
document = Document(test_docx("footnotes"))
50+
context.paragraphs = document.paragraphs
51+
context.footnotes = document.footnotes
52+
53+
54+
# when ====================================================
55+
56+
57+
@when("I try to access a footnote with invalid reference id")
58+
def when_I_try_to_access_a_footnote_with_invalid_reference_id(context: Context):
59+
context.exc = None
60+
try:
61+
context.footnotes[10]
62+
except IndexError as e:
63+
context.exc = e
64+
65+
66+
@when("I add a footnote to the paragraphs[{parId}] with text '{footnoteText}'")
67+
def when_I_add_a_footnote_to_the_paragraph_with_text_text(context: Context, parId: str, footnoteText: str):
68+
par = context.paragraphs[int(parId)]
69+
new_footnote = par.add_footnote()
70+
new_footnote.add_paragraph(footnoteText)
71+
72+
73+
@when("I change footnote property {propName} to {value}")
74+
def when_I_change_footnote_property_propName_to_value(context: Context, propName: str, value: str):
75+
context.section.__setattr__(propName, eval(value))
76+
77+
78+
# then =====================================================
79+
80+
81+
@then("len(footnotes) is {expectedLen}")
82+
def then_len_footnotes_is_len(context: Context, expectedLen: str):
83+
footnotes = context.footnotes
84+
assert len(footnotes) == int(expectedLen), f"expected len(footnotes) of {expectedLen}, got {len(footnotes)}"
85+
86+
87+
@then("I can access a footnote by footnote reference id")
88+
def then_I_can_access_a_footnote_by_footnote_reference_id(context: Context):
89+
footnotes = context.footnotes
90+
for refId in range(-1, 3):
91+
footnote = footnotes[refId]
92+
assert isinstance(footnote, Footnote)
93+
94+
95+
@then("I can access a paragraph in a specific footnote")
96+
def then_I_can_access_a_paragraph_in_a_specific_footnote(context: Context):
97+
footnotes = context.footnotes
98+
for refId in range(1, 3):
99+
footnote = footnotes[refId]
100+
assert isinstance(footnote.paragraphs[0], Paragraph)
101+
102+
103+
@then("it trows an {exceptionType}")
104+
def then_it_trows_an_IndexError(context: Context, exceptionType: str):
105+
exc = context.exc
106+
assert isinstance(exc, eval(exceptionType)), f"expected IndexError, got {type(exc)}"
107+
108+
109+
@then("I can access footnote property {propName} with value {value}")
110+
def then_I_can_access_footnote_propery_name_with_value_value(context: Context, propName: str, value: str):
111+
actual_value = context.section.__getattribute__(propName)
112+
expected = eval(value)
113+
assert actual_value == expected, f"expected section.{propName} {value}, got {expected}"
114+
115+
116+
@then("the document contains a footnote with footnote reference id of {refId} with text '{footnoteText}'")
117+
def then_the_document_contains_a_footnote_with_footnote_reference_id_of_refId_with_text_text(context: Context, refId: str, footnoteText: str):
118+
par = context.paragraphs[1]
119+
f = par.footnotes[0]
120+
assert f.id == int(refId), f"expected {refId}, got {f.id}"
121+
assert f.paragraphs[0].text == footnoteText, f"expected {footnoteText}, got {f.paragraphs[0].text}"
122+
123+
124+
@then("paragraphs[{parId}] has footnote reference ids of {refIds}, with footnote text {fText}")
125+
def then_paragraph_has_footnote_reference_ids_of_refIds_with_footnote_text_text(context: Context, parId: str, refIds: str, fText: str):
126+
par = context.paragraphs[int(parId)]
127+
refIds = eval(refIds)
128+
fText = eval(fText)
129+
if refIds is not None:
130+
if type(refIds) is list:
131+
for i in range(len(refIds)):
132+
f = par.footnotes[i]
133+
assert isinstance(f, Footnote), f"expected to be instance of Footnote, got {type(f)}"
134+
assert f.id == refIds[i], f"expected {refIds[i]}, got {f.id}"
135+
assert f.paragraphs[0].text == fText[i], f"expected '{fText[i]}', got '{f.paragraphs[0].text}'"
136+
else:
137+
f = par.footnotes[0]
138+
assert f.id == int(refIds), f"expected {refIds}, got {f.id}"
139+
assert f.paragraphs[0].text == fText, f"expected '{fText}', got '{f.paragraphs[0].text}'"
140+
else:
141+
assert len(par.footnotes) == 0, f"expected an empty list, got {len(par.footnotes)} elements"
15.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)