"""Unit test suite for the docx.styles.styles module.""" import pytest from docx.enum.style import WD_STYLE_TYPE from docx.oxml.styles import CT_Style, CT_Styles from docx.styles.latent import LatentStyles from docx.styles.style import BaseStyle from docx.styles.styles import Styles from ..unitutil.cxml import element from ..unitutil.mock import call, class_mock, function_mock, instance_mock, method_mock class DescribeStyles: def it_supports_the_in_operator_on_style_name(self, in_fixture): styles, name, expected_value = in_fixture assert (name in styles) is expected_value def it_knows_its_length(self, len_fixture): styles, expected_value = len_fixture assert len(styles) == expected_value def it_can_iterate_over_its_styles(self, iter_fixture): styles, expected_count, style_, StyleFactory_, expected_calls = iter_fixture count = 0 for style in styles: assert style is style_ count += 1 assert count == expected_count assert StyleFactory_.call_args_list == expected_calls @pytest.mark.filterwarnings("ignore::UserWarning") def it_can_get_a_style_by_id(self, getitem_id_fixture): styles, key, expected_element = getitem_id_fixture style = styles[key] assert style._element is expected_element def it_can_get_a_style_by_name(self, getitem_name_fixture): styles, key, expected_element = getitem_name_fixture style = styles[key] assert style._element is expected_element def it_raises_on_style_not_found(self, get_raises_fixture): styles, key = get_raises_fixture with pytest.raises(KeyError): styles[key] def it_can_add_a_new_style(self, add_fixture): styles, name, style_type, builtin = add_fixture[:4] name_, StyleFactory_, style_elm_, style_ = add_fixture[4:] style = styles.add_style(name, style_type, builtin) styles._element.add_style_of_type.assert_called_once_with(name_, style_type, builtin) StyleFactory_.assert_called_once_with(style_elm_) assert style is style_ def it_raises_when_style_name_already_used(self, add_raises_fixture): styles, name = add_raises_fixture with pytest.raises(ValueError, match="document already contains style 'Hea"): styles.add_style(name, None) def it_can_get_the_default_style_for_a_type(self, default_fixture): styles, style_type, StyleFactory_ = default_fixture[:3] StyleFactory_calls, style_ = default_fixture[3:] style = styles.default(style_type) assert StyleFactory_.call_args_list == StyleFactory_calls assert style is style_ def it_can_get_a_style_of_type_by_id(self, _get_by_id_, style_): style_id, style_type = 42, 7 _get_by_id_.return_value = style_ styles = Styles(None) style = styles.get_by_id(style_id, style_type) _get_by_id_.assert_called_once_with(styles, style_id, style_type) assert style is style_ def but_it_returns_the_default_style_for_style_id_None(self, default_, style_): style_type = 17 default_.return_value = style_ styles = Styles(None) style = styles.get_by_id(None, style_type) default_.assert_called_once_with(styles, style_type) assert style is style_ def it_can_get_a_style_id_from_a_style(self, _get_style_id_from_style_): style = BaseStyle(None) style_type = 22 _get_style_id_from_style_.return_value = "StyleId" styles = Styles(None) style_id = styles.get_style_id(style, style_type) _get_style_id_from_style_.assert_called_once_with(styles, style, style_type) assert style_id == "StyleId" def and_it_can_get_a_style_id_from_a_style_name(self, _get_style_id_from_name_): style_type = 22 _get_style_id_from_name_.return_value = "StyleId" styles = Styles(None) style_id = styles.get_style_id("Style Name", style_type) _get_style_id_from_name_.assert_called_once_with(styles, "Style Name", style_type) assert style_id == "StyleId" def but_it_returns_None_for_a_style_or_name_of_None(self): styles = Styles(None) style_id = styles.get_style_id(None, style_type=22) assert style_id is None def it_gets_a_style_by_id_to_help(self, _get_by_id_fixture): styles, style_id, style_type, default_calls = _get_by_id_fixture[:4] StyleFactory_, StyleFactory_calls, style_ = _get_by_id_fixture[4:] style = styles._get_by_id(style_id, style_type) assert styles.default.call_args_list == default_calls assert StyleFactory_.call_args_list == StyleFactory_calls assert style is style_ def it_gets_a_style_id_from_a_name_to_help(self, _getitem_, _get_style_id_from_style_, style_): style_name, style_type, style_id_ = "Foo Bar", 1, "FooBar" _getitem_.return_value = style_ _get_style_id_from_style_.return_value = style_id_ styles = Styles(None) style_id = styles._get_style_id_from_name(style_name, style_type) styles.__getitem__.assert_called_once_with(styles, style_name) _get_style_id_from_style_.assert_called_once_with(styles, style_, style_type) assert style_id is style_id_ def it_gets_a_style_id_from_a_style_to_help(self, id_style_fixture): styles, style_, style_type, style_id_ = id_style_fixture style_id = styles._get_style_id_from_style(style_, style_type) styles.default.assert_called_once_with(styles, style_type) assert style_id is style_id_ def it_raises_on_style_type_mismatch(self, id_style_raises_fixture): styles, style_, style_type = id_style_raises_fixture with pytest.raises(ValueError, match="assigned style is type 1, need type 2"): styles._get_style_id_from_style(style_, style_type) def it_provides_access_to_the_latent_styles(self, latent_styles_fixture): styles, LatentStyles_, latent_styles_ = latent_styles_fixture latent_styles = styles.latent_styles LatentStyles_.assert_called_once_with(styles._element.latentStyles) assert latent_styles is latent_styles_ # fixture -------------------------------------------------------- @pytest.fixture( params=[ ("Foo Bar", "Foo Bar", WD_STYLE_TYPE.CHARACTER, False), ("Heading 1", "heading 1", WD_STYLE_TYPE.PARAGRAPH, True), ] ) def add_fixture(self, request, styles_elm_, _getitem_, style_elm_, StyleFactory_, style_): name, name_, style_type, builtin = request.param styles = Styles(styles_elm_) _getitem_.return_value = None styles_elm_.add_style_of_type.return_value = style_elm_ StyleFactory_.return_value = style_ return ( styles, name, style_type, builtin, name_, StyleFactory_, style_elm_, style_, ) @pytest.fixture def add_raises_fixture(self, _getitem_): styles = Styles(element("w:styles/w:style/w:name{w:val=heading 1}")) name = "Heading 1" return styles, name @pytest.fixture( params=[ ("w:styles", False, WD_STYLE_TYPE.CHARACTER), ( "w:styles/w:style{w:type=paragraph,w:default=1}", True, WD_STYLE_TYPE.PARAGRAPH, ), ( "w:styles/(w:style{w:type=table,w:default=1},w:style{w:type=table,w:default=1})", True, WD_STYLE_TYPE.TABLE, ), ] ) def default_fixture(self, request, StyleFactory_, style_): styles_cxml, is_defined, style_type = request.param styles_elm = element(styles_cxml) styles = Styles(styles_elm) StyleFactory_calls = [call(styles_elm[-1])] if is_defined else [] StyleFactory_.return_value = style_ expected_value = style_ if is_defined else None return (styles, style_type, StyleFactory_, StyleFactory_calls, expected_value) @pytest.fixture( params=[ ( "w:styles/w:style{w:type=paragraph,w:styleId=Foo}", "Foo", WD_STYLE_TYPE.PARAGRAPH, ), ( "w:styles/w:style{w:type=paragraph,w:styleId=Foo}", "Bar", WD_STYLE_TYPE.PARAGRAPH, ), ( "w:styles/w:style{w:type=table,w:styleId=Bar}", "Bar", WD_STYLE_TYPE.PARAGRAPH, ), ] ) def _get_by_id_fixture(self, request, default_, StyleFactory_, style_): styles_cxml, style_id, style_type = request.param styles_elm = element(styles_cxml) style_elm = styles_elm[0] styles = Styles(styles_elm) default_calls = [] if style_id == "Foo" else [call(styles, style_type)] StyleFactory_calls = [call(style_elm)] if style_id == "Foo" else [] default_.return_value = StyleFactory_.return_value = style_ return ( styles, style_id, style_type, default_calls, StyleFactory_, StyleFactory_calls, style_, ) @pytest.fixture( params=[ ("w:styles/(w:style{%s,w:styleId=Foobar},w:style,w:style)", 0), ("w:styles/(w:style,w:style{%s,w:styleId=Foobar},w:style)", 1), ("w:styles/(w:style,w:style,w:style{%s,w:styleId=Foobar})", 2), ] ) def getitem_id_fixture(self, request): styles_cxml_tmpl, style_idx = request.param styles_cxml = styles_cxml_tmpl % "w:type=paragraph" styles = Styles(element(styles_cxml)) expected_element = styles._element[style_idx] return styles, "Foobar", expected_element @pytest.fixture( params=[ ("w:styles/(w:style%s/w:name{w:val=foo},w:style)", "foo", 0), ("w:styles/(w:style,w:style%s/w:name{w:val=foo})", "foo", 1), ("w:styles/w:style%s/w:name{w:val=heading 1}", "Heading 1", 0), ] ) def getitem_name_fixture(self, request): styles_cxml_tmpl, key, style_idx = request.param styles_cxml = styles_cxml_tmpl % "{w:type=character}" styles = Styles(element(styles_cxml)) expected_element = styles._element[style_idx] return styles, key, expected_element @pytest.fixture( params=[ ("w:styles/(w:style,w:style/w:name{w:val=foo},w:style)"), ("w:styles/(w:style{w:styleId=foo},w:style,w:style)"), ] ) def get_raises_fixture(self, request): styles_cxml = request.param styles = Styles(element(styles_cxml)) return styles, "bar" @pytest.fixture(params=[True, False]) def id_style_fixture(self, request, default_, style_): style_is_default = request.param styles = Styles(None) style_id, style_type = "FooBar", 1 default_.return_value = style_ if style_is_default else None style_.style_id, style_.type = style_id, style_type expected_value = None if style_is_default else style_id return styles, style_, style_type, expected_value @pytest.fixture def id_style_raises_fixture(self, style_): styles = Styles(None) style_.type = 1 style_type = 2 return styles, style_, style_type @pytest.fixture( params=[ ("w:styles/w:style/w:name{w:val=heading 1}", "Heading 1", True), ("w:styles/w:style/w:name{w:val=Foo Bar}", "Foo Bar", True), ("w:styles/w:style/w:name{w:val=heading 1}", "Foobar", False), ("w:styles", "Foobar", False), ] ) def in_fixture(self, request): styles_cxml, name, expected_value = request.param styles = Styles(element(styles_cxml)) return styles, name, expected_value @pytest.fixture( params=[ ("w:styles", 0), ("w:styles/w:style", 1), ("w:styles/(w:style,w:style)", 2), ("w:styles/(w:style,w:style,w:style)", 3), ] ) def iter_fixture(self, request, StyleFactory_, style_): styles_cxml, expected_count = request.param styles_elm = element(styles_cxml) styles = Styles(styles_elm) expected_calls = [call(style_elm) for style_elm in styles_elm] StyleFactory_.return_value = style_ return styles, expected_count, style_, StyleFactory_, expected_calls @pytest.fixture def latent_styles_fixture(self, LatentStyles_, latent_styles_): styles = Styles(element("w:styles/w:latentStyles")) return styles, LatentStyles_, latent_styles_ @pytest.fixture( params=[ ("w:styles", 0), ("w:styles/w:style", 1), ("w:styles/(w:style,w:style)", 2), ("w:styles/(w:style,w:style,w:style)", 3), ] ) def len_fixture(self, request): styles_cxml, expected_value = request.param styles = Styles(element(styles_cxml)) return styles, expected_value # fixture components --------------------------------------------- @pytest.fixture def default_(self, request): return method_mock(request, Styles, "default") @pytest.fixture def _get_by_id_(self, request): return method_mock(request, Styles, "_get_by_id") @pytest.fixture def _getitem_(self, request): return method_mock(request, Styles, "__getitem__") @pytest.fixture def _get_style_id_from_name_(self, request): return method_mock(request, Styles, "_get_style_id_from_name") @pytest.fixture def _get_style_id_from_style_(self, request): return method_mock(request, Styles, "_get_style_id_from_style") @pytest.fixture def LatentStyles_(self, request, latent_styles_): return class_mock(request, "docx.styles.styles.LatentStyles", return_value=latent_styles_) @pytest.fixture def latent_styles_(self, request): return instance_mock(request, LatentStyles) @pytest.fixture def style_(self, request): return instance_mock(request, BaseStyle) @pytest.fixture def StyleFactory_(self, request): return function_mock(request, "docx.styles.styles.StyleFactory") @pytest.fixture def style_elm_(self, request): return instance_mock(request, CT_Style) @pytest.fixture def styles_elm_(self, request): return instance_mock(request, CT_Styles)