diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/docx/body_xml_tests.py | 234 | ||||
| -rw-r--r-- | tests/docx/styles_xml_tests.py | 31 |
2 files changed, 145 insertions, 120 deletions
diff --git a/tests/docx/body_xml_tests.py b/tests/docx/body_xml_tests.py index b259d59..7ccd9b4 100644 --- a/tests/docx/body_xml_tests.py +++ b/tests/docx/body_xml_tests.py @@ -55,41 +55,39 @@ class ParagraphTests(object): def paragraph_has_no_style_if_it_has_no_properties(self): element = xml_element("w:p") assert_equal(None, _read_and_get_document_xml_element(element).style_id) - + @istest def paragraph_has_style_id_and_name_read_from_paragraph_properties_if_present(self): style_xml = xml_element("w:pStyle", {"w:val": "Heading1"}) properties_xml = xml_element("w:pPr", {}, [style_xml]) paragraph_xml = xml_element("w:p", {}, [properties_xml]) - - styles = Styles( + + styles = Styles.create( paragraph_styles={"Heading1": Style(style_id="Heading1", name="Heading 1")}, - character_styles={}, - table_styles={}, ) - + paragraph = _read_and_get_document_xml_element(paragraph_xml, styles=styles) assert_equal("Heading1", paragraph.style_id) assert_equal("Heading 1", paragraph.style_name) - + @istest def warning_is_emitted_when_paragraph_style_cannot_be_found(self): style_xml = xml_element("w:pStyle", {"w:val": "Heading1"}) properties_xml = xml_element("w:pPr", {}, [style_xml]) paragraph_xml = xml_element("w:p", {}, [properties_xml]) - + result = _read_document_xml_element(paragraph_xml, styles=Styles.EMPTY) paragraph = result.value assert_equal("Heading1", paragraph.style_id) assert_equal(None, paragraph.style_name) assert_equal([results.warning("Paragraph style with ID Heading1 was referenced but not defined in the document")], result.messages) - + @istest def paragraph_has_no_justification_if_it_has_no_justificiation_properties(self): paragraph_xml = xml_element("w:p") paragraph = _read_and_get_document_xml_element(paragraph_xml) assert_equal(None, paragraph.alignment) - + @istest def paragraph_has_justification_read_from_paragraph_properties_if_present(self): justification_xml = xml_element("w:jc", {"w:val": "center"}) @@ -97,47 +95,47 @@ class ParagraphTests(object): paragraph_xml = xml_element("w:p", {}, [properties_xml]) paragraph = _read_and_get_document_xml_element(paragraph_xml) assert_equal("center", paragraph.alignment) - + @istest def paragraph_has_no_numbering_if_it_has_no_numbering_properties(self): element = xml_element("w:p") assert_equal(None, _read_and_get_document_xml_element(element).numbering) - + @istest def paragraph_has_numbering_properties_from_paragraph_properties_if_present(self): paragraph_xml = self._paragraph_with_numbering_properties([ xml_element("w:ilvl", {"w:val": "1"}), xml_element("w:numId", {"w:val": "42"}), ]) - + numbering = Numbering({"42": {"1": documents.numbering_level("1", True)}}) paragraph = _read_and_get_document_xml_element(paragraph_xml, numbering=numbering) - + assert_equal("1", paragraph.numbering.level_index) assert_equal(True, paragraph.numbering.is_ordered) - + @istest def numbering_properties_are_ignored_if_lvl_is_missing(self): paragraph_xml = self._paragraph_with_numbering_properties([ xml_element("w:numId", {"w:val": "42"}), ]) - + numbering = Numbering({"42": {"1": documents.numbering_level("1", True)}}) paragraph = _read_and_get_document_xml_element(paragraph_xml, numbering=numbering) - + assert_equal(None, paragraph.numbering) - + @istest def numbering_properties_are_ignored_if_num_id_is_missing(self): paragraph_xml = self._paragraph_with_numbering_properties([ xml_element("w:ilvl", {"w:val": "1"}), ]) - + numbering = Numbering({"42": {"1": documents.numbering_level("1", True)}}) paragraph = _read_and_get_document_xml_element(paragraph_xml, numbering=numbering) - + assert_equal(None, paragraph.numbering) - + def _paragraph_with_numbering_properties(self, children): numbering_properties_xml = xml_element("w:numPr", {}, children) properties_xml = xml_element("w:pPr", {}, [numbering_properties_xml]) @@ -151,7 +149,7 @@ class ParagraphIndentTests(object): paragraph_xml = self._paragraph_with_indent({"w:start": "720", "w:left": "40"}) paragraph = _read_and_get_document_xml_element(paragraph_xml) assert_equal("720", paragraph.indent.start) - + @istest def when_w_start_is_not_set_then_start_indent_is_read_from_w_left(self): paragraph_xml = self._paragraph_with_indent({"w:left": "720"}) @@ -190,7 +188,7 @@ class ParagraphIndentTests(object): assert_equal(None, paragraph.indent.end) assert_equal(None, paragraph.indent.first_line) assert_equal(None, paragraph.indent.hanging) - + def _paragraph_with_indent(self, attributes): indent_xml = xml_element("w:ind", attributes) properties_xml = xml_element("w:pPr", {}, [indent_xml]) @@ -203,79 +201,77 @@ class RunTests(object): def run_has_no_style_if_it_has_no_properties(self): element = xml_element("w:r") assert_equal(None, _read_and_get_document_xml_element(element).style_id) - + @istest def run_has_style_id_and_name_read_from_run_properties_if_present(self): style_xml = xml_element("w:rStyle", {"w:val": "Heading1Char"}) - - styles = Styles( - paragraph_styles={}, + + styles = Styles.create( character_styles={"Heading1Char": Style(style_id="Heading1Char", name="Heading 1 Char")}, - table_styles={}, ) - + run = self._read_run_with_properties([style_xml], styles=styles) assert_equal("Heading1Char", run.style_id) assert_equal("Heading 1 Char", run.style_name) - + @istest def warning_is_emitted_when_run_style_cannot_be_found(self): style_xml = xml_element("w:rStyle", {"w:val": "Heading1Char"}) properties_xml = xml_element("w:rPr", {}, [style_xml]) run_xml = xml_element("w:r", {}, [properties_xml]) - + result = _read_document_xml_element(run_xml, styles=Styles.EMPTY) run = result.value assert_equal("Heading1Char", run.style_id) assert_equal(None, run.style_name) assert_equal([results.warning("Run style with ID Heading1Char was referenced but not defined in the document")], result.messages) - - + + @istest def run_is_not_bold_if_bold_element_is_not_present(self): run = self._read_run_with_properties([]) assert_equal(False, run.is_bold) - + @istest def run_is_bold_if_bold_element_is_present(self): run = self._read_run_with_properties([xml_element("w:b")]) assert_equal(True, run.is_bold) - + @istest def run_is_not_italic_if_italic_element_is_not_present(self): run = self._read_run_with_properties([]) assert_equal(False, run.is_italic) - + @istest def run_is_italic_if_italic_element_is_present(self): run = self._read_run_with_properties([xml_element("w:i")]) assert_equal(True, run.is_italic) - + @istest def run_is_not_underlined_if_underline_element_is_not_present(self): run = self._read_run_with_properties([]) assert_equal(False, run.is_underline) - + @istest def run_is_underlined_if_underline_element_is_present(self): run = self._read_run_with_properties([xml_element("w:u")]) assert_equal(True, run.is_underline) - + @istest def run_is_not_struckthrough_if_strikethrough_element_is_not_present(self): run = self._read_run_with_properties([]) assert_equal(False, run.is_strikethrough) - + @istest def run_is_struckthrough_if_strikethrough_element_is_present(self): run = self._read_run_with_properties([xml_element("w:strike")]) assert_equal(True, run.is_strikethrough) - + @istest def run_is_not_small_caps_if_small_caps_element_is_not_present(self): run = self._read_run_with_properties([]) assert_equal(False, run.is_small_caps) - + @istest def run_is_small_caps_if_small_caps_element_is_present(self): run = self._read_run_with_properties([xml_element("w:smallCaps")]) @@ -308,12 +304,12 @@ class RunTests(object): def run_boolean_property_is_true_if_element_is_present_and_val_is_1(self, attr_name, tag_name): run = self._read_run_with_properties([xml_element(tag_name, {"w:val": "1"})]) assert_equal(True, getattr(run, attr_name)) - + @istest def run_has_baseline_vertical_alignment_if_vertical_alignment_element_is_not_present(self): run = self._read_run_with_properties([]) assert_equal(documents.VerticalAlignment.baseline, run.vertical_alignment) - + @istest def run_has_vertical_alignment_read_from_vertical_alignment_element(self): run = self._read_run_with_properties([xml_element("w:vertAlign", {"w:val": "superscript"})]) @@ -329,7 +325,7 @@ class RunTests(object): font_xml = xml_element("w:rFonts", {"w:ascii": "Arial"}) run = self._read_run_with_properties([font_xml]) assert_equal("Arial", run.font) - + def _read_run_with_properties(self, properties, styles=None): properties_xml = xml_element("w:rPr", {}, properties) run_xml = xml_element("w:r", {}, [properties_xml]) @@ -351,7 +347,7 @@ class ComplexFieldTests(object): _HYPERLINK_INSTRTEXT = xml_element("w:instrText", {}, [ xml_text(' HYPERLINK "{0}"'.format(_URI)) ]) - + def _is_hyperlinked_run(self, **kwargs): return is_run(children=is_sequence( is_hyperlink( @@ -359,11 +355,11 @@ class ComplexFieldTests(object): **kwargs ), )) - + @property def _is_empty_hyperlinked_run(self): return self._is_hyperlinked_run(children=[]) - + @istest def runs_in_a_complex_field_for_hyperlinks_are_read_as_hyperlinks(self): element = xml_element("w:p", {}, [ @@ -383,7 +379,7 @@ class ComplexFieldTests(object): )), is_empty_run, ))) - + @istest def runs_after_a_complex_field_for_hyperlinks_are_not_read_as_hyperlinks(self): element = xml_element("w:p", {}, [ @@ -403,7 +399,7 @@ class ComplexFieldTests(object): is_text("this will not be a hyperlink"), )), ))) - + @istest def can_handle_split_instr_text_elements(self): element = xml_element("w:p", {}, [ @@ -428,7 +424,7 @@ class ComplexFieldTests(object): )), is_empty_run, ))) - + @istest def hyperlink_is_not_ended_by_end_of_nested_complex_field(self): element = xml_element("w:p", {}, [ @@ -457,7 +453,7 @@ class ComplexFieldTests(object): )), is_empty_run, ))) - + @istest def complex_field_nested_within_a_hyperlink_complex_field_is_wrapped_with_the_hyperlink(self): element = xml_element("w:p", {}, [ @@ -486,7 +482,7 @@ class ComplexFieldTests(object): self._is_empty_hyperlinked_run, is_empty_run, ))) - + @istest def field_without_separate_fld_char_is_ignored(self): element = xml_element("w:p", {}, [ @@ -524,7 +520,7 @@ def no_break_hyphen_element_is_read_as_non_breaking_hyphen_character(): element = xml_element("w:noBreakHyphen") tab = _read_and_get_document_xml_element(element) assert_equal(documents.text(unichr(0x2011)), tab) - + @istest class TableTests(object): @@ -546,40 +542,38 @@ class TableTests(object): ]) ]) assert_equal(expected_result, table) - + @istest def table_has_no_style_if_it_has_no_properties(self): element = xml_element("w:tbl") assert_equal(None, _read_and_get_document_xml_element(element).style_id) - + @istest def table_has_style_id_and_name_read_from_paragraph_properties_if_present(self): style_xml = xml_element("w:tblStyle", {"w:val": "TableNormal"}) properties_xml = xml_element("w:tblPr", {}, [style_xml]) table_xml = xml_element("w:tbl", {}, [properties_xml]) - - styles = Styles( - paragraph_styles={}, - character_styles={}, + + styles = Styles.create( table_styles={"TableNormal": Style(style_id="TableNormal", name="Normal Table")}, ) - + paragraph = _read_and_get_document_xml_element(table_xml, styles=styles) assert_equal("TableNormal", paragraph.style_id) assert_equal("Normal Table", paragraph.style_name) - + @istest def warning_is_emitted_when_table_style_cannot_be_found(self): style_xml = xml_element("w:tblStyle", {"w:val": "TableNormal"}) properties_xml = xml_element("w:tblPr", {}, [style_xml]) table_xml = xml_element("w:tbl", {}, [properties_xml]) - + result = _read_document_xml_element(table_xml, styles=Styles.EMPTY) table = result.value assert_equal("TableNormal", table.style_id) assert_equal(None, table.style_name) assert_equal([results.warning("Table style with ID TableNormal was referenced but not defined in the document")], result.messages) - + @istest def tbl_header_marks_table_row_as_header(self): element = xml_element("w:tbl", {}, [ @@ -597,8 +591,8 @@ class TableTests(object): is_row(is_header=False), ), )) - - + + @istest def gridspan_is_read_as_colspan_for_table_cell(self): element = xml_element("w:tbl", {}, [ @@ -707,7 +701,7 @@ class TableTests(object): @istest def children_of_w_ins_are_converted_normally(): _assert_children_are_converted_normally("w:ins") - + @istest def children_of_w_object_are_converted_normally(): _assert_children_are_converted_normally("w:object") @@ -752,7 +746,7 @@ class HyperlinkTests(object): documents.hyperlink(href="http://example.com", children=[documents.run([])]), _read_and_get_document_xml_element(element, relationships=relationships) ) - + @istest def hyperlink_is_read_as_external_hyperlink_if_it_has_a_relationship_id_and_an_anchor(self): relationships = Relationships([ @@ -764,7 +758,7 @@ class HyperlinkTests(object): documents.hyperlink(href="http://example.com/#fragment", children=[documents.run([])]), _read_and_get_document_xml_element(element, relationships=relationships) ) - + @istest def existing_fragment_is_replaced_when_anchor_is_set_on_external_link(self): relationships = Relationships([ @@ -776,7 +770,7 @@ class HyperlinkTests(object): documents.hyperlink(href="http://example.com/#fragment", children=[documents.run([])]), _read_and_get_document_xml_element(element, relationships=relationships) ) - + @istest def hyperlink_is_read_as_internal_hyperlink_if_it_has_an_anchor_attribute(self): run_element = xml_element("w:r") @@ -785,7 +779,7 @@ class HyperlinkTests(object): documents.hyperlink(anchor="start", children=[documents.run([])]), _read_and_get_document_xml_element(element) ) - + @istest def hyperlink_is_ignored_if_it_does_not_have_a_relationship_id_nor_anchor(self): run_element = xml_element("w:r") @@ -794,7 +788,7 @@ class HyperlinkTests(object): documents.run([]), _read_and_get_document_xml_element(element) ) - + @istest def target_frame_is_read(self): element = xml_element("w:hyperlink", { @@ -805,7 +799,7 @@ class HyperlinkTests(object): _read_and_get_document_xml_element(element), is_hyperlink(target_frame="_blank"), ) - + @istest def empty_target_frame_is_ignored(self): element = xml_element("w:hyperlink", { @@ -824,7 +818,7 @@ class BookmarkTests(object): def go_back_bookmark_is_ignored(self): element = xml_element("w:bookmarkStart", {"w:name": "_GoBack"}) assert_equal(None, _read_and_get_document_xml_element(element)) - + @istest def bookmark_start_is_read_if_name_is_not_go_back(self): element = xml_element("w:bookmarkStart", {"w:name": "start"}) @@ -873,67 +867,67 @@ class BreakTests(object): class ImageTests(object): IMAGE_BYTES = b"Not an image at all!" IMAGE_RELATIONSHIP_ID = "rId5" - + def _read_embedded_image(self, element): relationships = Relationships([ _image_relationship(self.IMAGE_RELATIONSHIP_ID, "media/hat.png"), ]) - + mocks = funk.Mocks() docx_file = mocks.mock() funk.allows(docx_file).open("word/media/hat.png").returns(io.BytesIO(self.IMAGE_BYTES)) - + content_types = mocks.mock() funk.allows(content_types).find_content_type("word/media/hat.png").returns("image/png") - + return _read_and_get_document_xml_element( element, content_types=content_types, relationships=relationships, docx_file=docx_file, ) - + @istest def can_read_imagedata_elements_with_rid_attribute(self): imagedata_element = xml_element("v:imagedata", { "r:id": self.IMAGE_RELATIONSHIP_ID, "o:title": "It's a hat" }) - + image = self._read_embedded_image(imagedata_element) - + assert_equal(documents.Image, type(image)) assert_equal("It's a hat", image.alt_text) assert_equal("image/png", image.content_type) with image.open() as image_file: assert_equal(self.IMAGE_BYTES, image_file.read()) - + @istest def when_imagedata_element_has_no_relationship_id_then_it_is_ignored_with_warning(self): imagedata_element = xml_element("v:imagedata") - + result = _read_document_xml_element(imagedata_element) expected_warning = results.warning("A v:imagedata element without a relationship ID was ignored") assert_equal([expected_warning], result.messages) assert_equal(None, result.value) - - + + @istest def can_read_inline_pictures(self): drawing_element = _create_inline_image( blip=_embedded_blip(self.IMAGE_RELATIONSHIP_ID), description="It's a hat", ) - + image = self._read_embedded_image(drawing_element) - + assert_equal(documents.Image, type(image)) assert_equal("It's a hat", image.alt_text) assert_equal("image/png", image.content_type) with image.open() as image_file: assert_equal(self.IMAGE_BYTES, image_file.read()) - - + + @istest def alt_text_title_is_used_if_alt_text_description_is_blank(self): drawing_element = _create_inline_image( @@ -941,32 +935,32 @@ class ImageTests(object): description=" ", title="It's a hat", ) - + image = self._read_embedded_image(drawing_element) - + assert_equal(documents.Image, type(image)) assert_equal("It's a hat", image.alt_text) assert_equal("image/png", image.content_type) with image.open() as image_file: assert_equal(self.IMAGE_BYTES, image_file.read()) - - + + @istest def alt_text_title_is_used_if_alt_text_description_is_missing(self): drawing_element = _create_inline_image( blip=_embedded_blip(self.IMAGE_RELATIONSHIP_ID), title="It's a hat", ) - + image = self._read_embedded_image(drawing_element) - + assert_equal(documents.Image, type(image)) assert_equal("It's a hat", image.alt_text) assert_equal("image/png", image.content_type) with image.open() as image_file: assert_equal(self.IMAGE_BYTES, image_file.read()) - - + + @istest def alt_text_description_is_preferred_to_alt_text_title(self): drawing_element = _create_inline_image( @@ -974,31 +968,31 @@ class ImageTests(object): description="It's a hat", title="hat", ) - + image = self._read_embedded_image(drawing_element) - + assert_equal(documents.Image, type(image)) assert_equal("It's a hat", image.alt_text) assert_equal("image/png", image.content_type) with image.open() as image_file: assert_equal(self.IMAGE_BYTES, image_file.read()) - + @istest def can_read_anchored_pictures(self): drawing_element = _create_anchored_image( blip=_embedded_blip(self.IMAGE_RELATIONSHIP_ID), description="It's a hat", ) - + image = self._read_embedded_image(drawing_element) - + assert_equal(documents.Image, type(image)) assert_equal("It's a hat", image.alt_text) assert_equal("image/png", image.content_type) with image.open() as image_file: assert_equal(self.IMAGE_BYTES, image_file.read()) - - + + @istest @funk.with_mocks def warning_if_unsupported_image_type(self, mocks): @@ -1006,17 +1000,17 @@ class ImageTests(object): blip=_embedded_blip("rId5"), description="It's a hat", ) - + relationships = Relationships([ _image_relationship("rId5", "media/hat.emf"), ]) - + docx_file = mocks.mock() funk.allows(docx_file).open("word/media/hat.emf").returns(io.BytesIO(self.IMAGE_BYTES)) - + content_types = mocks.mock() funk.allows(content_types).find_content_type("word/media/hat.emf").returns("image/x-emf") - + result = _read_document_xml_element( drawing_element, content_types=content_types, @@ -1026,8 +1020,8 @@ class ImageTests(object): assert_equal("image/x-emf", result.value.content_type) expected_warning = results.warning("Image of type image/x-emf is unlikely to display in web browsers") assert_equal([expected_warning], result.messages) - - + + @istest @funk.with_mocks def can_read_linked_pictures(self, mocks): @@ -1035,18 +1029,18 @@ class ImageTests(object): blip=_linked_blip("rId5"), description="It's a hat", ) - + relationships = Relationships([ _image_relationship("rId5", "file:///media/hat.png"), ]) - + files = mocks.mock() funk.allows(files).verify("file:///media/hat.png") funk.allows(files).open("file:///media/hat.png").returns(io.BytesIO(self.IMAGE_BYTES)) - + content_types = mocks.mock() funk.allows(content_types).find_content_type("file:///media/hat.png").returns("image/png") - + image = _read_and_get_document_xml_element( drawing_element, content_types=content_types, @@ -1098,7 +1092,7 @@ def unrecognised_children_are_ignored(): documents.run([documents.Text("Hello!")]), _read_document_xml_element(element).value ) - + @istest def text_boxes_have_content_appended_after_containing_paragraph(): text_box = xml_element("w:pict", {}, [ @@ -1190,10 +1184,10 @@ def _read_document_xml(func, element, *args, **kwargs): class FakeStyles(object): def find_paragraph_style_by_id(self, style_id): return Style(style_id, style_id) - + def find_character_style_by_id(self, style_id): return None - + def find_table_style_by_id(self, style_id): return None @@ -1217,7 +1211,7 @@ def _run_element_with_text(text): def _text_element(value): return xml_element("w:t", {}, [xml_text(value)]) - + def _create_inline_image(blip, description=None, title=None): return xml_element("w:drawing", {}, [ @@ -1230,14 +1224,14 @@ def _create_anchored_image(description, blip): xml_element("wp:anchor", {}, _create_image_elements(blip, description=description, )) ]) - + def _create_image_elements(blip, description=None, title=None): properties = {} if description is not None: properties["descr"] = description if title is not None: properties["title"] = title - + return [ xml_element("wp:docPr", properties), xml_element("a:graphic", {}, [ diff --git a/tests/docx/styles_xml_tests.py b/tests/docx/styles_xml_tests.py index d70e068..580d91d 100644 --- a/tests/docx/styles_xml_tests.py +++ b/tests/docx/styles_xml_tests.py @@ -81,6 +81,37 @@ def style_name_is_none_if_name_element_does_not_exist(): assert_equal(None, styles.find_character_style_by_id("Heading1Char").name) +@istest +def numbering_style_is_none_if_no_style_with_that_id_exists(): + element = xml_element("w:styles", {}, []) + styles = read_styles_xml_element(element) + assert_equal(None, styles.find_numbering_style_by_id("List1")) + + +@istest +def numbering_style_has_none_num_id_if_style_has_no_paragraph_properties(): + element = xml_element("w:styles", {}, [ + xml_element("w:style", {"w:type": "numbering", "w:styleId": "List1"}), + ]) + styles = read_styles_xml_element(element) + assert_equal(None, styles.find_numbering_style_by_id("List1").num_id) + + +@istest +def numbering_style_has_num_id_read_from_paragraph_properties(): + element = xml_element("w:styles", {}, [ + xml_element("w:style", {"w:type": "numbering", "w:styleId": "List1"}, [ + xml_element("w:pPr", {}, [ + xml_element("w:numPr", {}, [ + xml_element("w:numId", {"w:val": "42"}) + ]), + ]), + ]), + ]) + styles = read_styles_xml_element(element) + assert_equal("42", styles.find_numbering_style_by_id("List1").num_id) + + def _paragraph_style_element(style_id, name): return _style_element("paragraph", style_id, name) |
