1# encoding: utf-8
2
3"""
4Test suite for pptx.oxml.__init__.py module, primarily XML parser-related.
5"""
6
7from __future__ import print_function, unicode_literals
8
9import pytest
10
11from lxml import etree
12
13from docx.oxml import (
14    OxmlElement, oxml_parser, parse_xml, register_element_cls
15)
16from docx.oxml.ns import qn
17from docx.oxml.shared import BaseOxmlElement
18
19
20class DescribeOxmlElement(object):
21
22    def it_returns_an_lxml_element_with_matching_tag_name(self):
23        element = OxmlElement('a:foo')
24        assert isinstance(element, etree._Element)
25        assert element.tag == (
26            '{http://schemas.openxmlformats.org/drawingml/2006/main}foo'
27        )
28
29    def it_adds_supplied_attributes(self):
30        element = OxmlElement('a:foo', {'a': 'b', 'c': 'd'})
31        assert etree.tostring(element) == (
32            '<a:foo xmlns:a="http://schemas.openxmlformats.org/drawingml/200'
33            '6/main" a="b" c="d"/>'
34        ).encode('utf-8')
35
36    def it_adds_additional_namespace_declarations_when_supplied(self):
37        ns1 = 'http://schemas.openxmlformats.org/drawingml/2006/main'
38        ns2 = 'other'
39        element = OxmlElement('a:foo', nsdecls={'a': ns1, 'x': ns2})
40        assert len(element.nsmap.items()) == 2
41        assert element.nsmap['a'] == ns1
42        assert element.nsmap['x'] == ns2
43
44
45class DescribeOxmlParser(object):
46
47    def it_strips_whitespace_between_elements(self, whitespace_fixture):
48        pretty_xml_text, stripped_xml_text = whitespace_fixture
49        element = etree.fromstring(pretty_xml_text, oxml_parser)
50        xml_text = etree.tostring(element, encoding='unicode')
51        assert xml_text == stripped_xml_text
52
53    # fixtures -------------------------------------------------------
54
55    @pytest.fixture
56    def whitespace_fixture(self):
57        pretty_xml_text = (
58            '<foø>\n'
59            '  <bår>text</bår>\n'
60            '</foø>\n'
61        )
62        stripped_xml_text = '<foø><bår>text</bår></foø>'
63        return pretty_xml_text, stripped_xml_text
64
65
66class DescribeParseXml(object):
67
68    def it_accepts_bytes_and_assumes_utf8_encoding(self, xml_bytes):
69        parse_xml(xml_bytes)
70
71    def it_accepts_unicode_providing_there_is_no_encoding_declaration(self):
72        non_enc_decl = '<?xml version="1.0" standalone="yes"?>'
73        enc_decl = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
74        xml_body = '<foo><bar>føøbår</bar></foo>'
75        # unicode body by itself doesn't raise
76        parse_xml(xml_body)
77        # adding XML decl without encoding attr doesn't raise either
78        xml_text = '%s\n%s' % (non_enc_decl, xml_body)
79        parse_xml(xml_text)
80        # but adding encoding in the declaration raises ValueError
81        xml_text = '%s\n%s' % (enc_decl, xml_body)
82        with pytest.raises(ValueError):
83            parse_xml(xml_text)
84
85    def it_uses_registered_element_classes(self, xml_bytes):
86        register_element_cls('a:foo', CustElmCls)
87        element = parse_xml(xml_bytes)
88        assert isinstance(element, CustElmCls)
89
90    # fixture components ---------------------------------------------
91
92    @pytest.fixture
93    def xml_bytes(self):
94        return (
95            '<a:foo xmlns:a="http://schemas.openxmlformats.org/drawingml/200'
96            '6/main">\n'
97            '  <a:bar>foøbår</a:bar>\n'
98            '</a:foo>\n'
99        ).encode('utf-8')
100
101
102class DescribeRegisterElementCls(object):
103
104    def it_determines_class_used_for_elements_with_matching_tagname(
105            self, xml_text):
106        register_element_cls('a:foo', CustElmCls)
107        foo = parse_xml(xml_text)
108        assert type(foo) is CustElmCls
109        assert type(foo.find(qn('a:bar'))) is etree._Element
110
111    # fixture components ---------------------------------------------
112
113    @pytest.fixture
114    def xml_text(self):
115        return (
116            '<a:foo xmlns:a="http://schemas.openxmlformats.org/drawingml/200'
117            '6/main">\n'
118            '  <a:bar>foøbår</a:bar>\n'
119            '</a:foo>\n'
120        )
121
122
123# ===========================================================================
124# static fixture
125# ===========================================================================
126
127class CustElmCls(BaseOxmlElement):
128    pass
129