"""Test suite for the docx.text.tabstops module.""" import pytest from docx.enum.text import WD_TAB_ALIGNMENT, WD_TAB_LEADER from docx.shared import Twips from docx.text.tabstops import TabStop, TabStops from ..unitutil.cxml import element, xml from ..unitutil.mock import call, class_mock, instance_mock class DescribeTabStop: def it_knows_its_position(self, position_get_fixture): tab_stop, expected_value = position_get_fixture assert tab_stop.position == expected_value def it_can_change_its_position(self, position_set_fixture): tab_stop, value, tabs, new_idx, expected_xml = position_set_fixture tab_stop.position = value assert tab_stop._tab is tabs[new_idx] assert tabs.xml == expected_xml def it_knows_its_alignment(self, alignment_get_fixture): tab_stop, expected_value = alignment_get_fixture assert tab_stop.alignment == expected_value def it_can_change_its_alignment(self, alignment_set_fixture): tab_stop, value, expected_xml = alignment_set_fixture tab_stop.alignment = value assert tab_stop._element.xml == expected_xml def it_knows_its_leader(self, leader_get_fixture): tab_stop, expected_value = leader_get_fixture assert tab_stop.leader == expected_value def it_can_change_its_leader(self, leader_set_fixture): tab_stop, value, expected_xml = leader_set_fixture tab_stop.leader = value assert tab_stop._element.xml == expected_xml # fixture -------------------------------------------------------- @pytest.fixture( params=[ ("w:tab{w:val=left}", "LEFT"), ("w:tab{w:val=right}", "RIGHT"), ] ) def alignment_get_fixture(self, request): tab_stop_cxml, member = request.param tab_stop = TabStop(element(tab_stop_cxml)) expected_value = getattr(WD_TAB_ALIGNMENT, member) return tab_stop, expected_value @pytest.fixture( params=[ ("w:tab{w:val=left}", "RIGHT", "w:tab{w:val=right}"), ("w:tab{w:val=right}", "LEFT", "w:tab{w:val=left}"), ] ) def alignment_set_fixture(self, request): tab_stop_cxml, member, expected_cxml = request.param tab_stop = TabStop(element(tab_stop_cxml)) expected_xml = xml(expected_cxml) value = getattr(WD_TAB_ALIGNMENT, member) return tab_stop, value, expected_xml @pytest.fixture( params=[ ("w:tab", "SPACES"), ("w:tab{w:leader=none}", "SPACES"), ("w:tab{w:leader=dot}", "DOTS"), ] ) def leader_get_fixture(self, request): tab_stop_cxml, member = request.param tab_stop = TabStop(element(tab_stop_cxml)) expected_value = getattr(WD_TAB_LEADER, member) return tab_stop, expected_value @pytest.fixture( params=[ ("w:tab", "DOTS", "w:tab{w:leader=dot}"), ("w:tab{w:leader=dot}", "DASHES", "w:tab{w:leader=hyphen}"), ("w:tab{w:leader=hyphen}", "SPACES", "w:tab"), ("w:tab{w:leader=hyphen}", None, "w:tab"), ("w:tab", "SPACES", "w:tab"), ("w:tab", None, "w:tab"), ] ) def leader_set_fixture(self, request): tab_stop_cxml, new_value, expected_cxml = request.param tab_stop = TabStop(element(tab_stop_cxml)) value = None if new_value is None else getattr(WD_TAB_LEADER, new_value) expected_xml = xml(expected_cxml) return tab_stop, value, expected_xml @pytest.fixture def position_get_fixture(self, request): tab_stop = TabStop(element("w:tab{w:pos=720}")) return tab_stop, Twips(720) @pytest.fixture( params=[ ( "w:tabs/w:tab{w:pos=360,w:val=left}", Twips(720), 0, "w:tabs/w:tab{w:pos=720,w:val=left}", ), ( "w:tabs/(w:tab{w:pos=360,w:val=left},w:tab{w:pos=720,w:val=left})", Twips(180), 0, "w:tabs/(w:tab{w:pos=180,w:val=left},w:tab{w:pos=720,w:val=left})", ), ( "w:tabs/(w:tab{w:pos=360,w:val=left},w:tab{w:pos=720,w:val=left})", Twips(960), 1, "w:tabs/(w:tab{w:pos=720,w:val=left},w:tab{w:pos=960,w:val=left})", ), ( "w:tabs/(w:tab{w:pos=-72,w:val=left},w:tab{w:pos=-36,w:val=left})", Twips(-48), 0, "w:tabs/(w:tab{w:pos=-48,w:val=left},w:tab{w:pos=-36,w:val=left})", ), ( "w:tabs/(w:tab{w:pos=-72,w:val=left},w:tab{w:pos=-36,w:val=left})", Twips(-16), 1, "w:tabs/(w:tab{w:pos=-36,w:val=left},w:tab{w:pos=-16,w:val=left})", ), ] ) def position_set_fixture(self, request): tabs_cxml, value, new_idx, expected_cxml = request.param tabs = element(tabs_cxml) tab = tabs.tab_lst[0] tab_stop = TabStop(tab) expected_xml = xml(expected_cxml) return tab_stop, value, tabs, new_idx, expected_xml class DescribeTabStops: def it_knows_its_length(self, len_fixture): tab_stops, expected_value = len_fixture assert len(tab_stops) == expected_value def it_can_iterate_over_its_tab_stops(self, iter_fixture): tab_stops, expected_count, tab_stop_, TabStop_, expected_calls = iter_fixture count = 0 for tab_stop in tab_stops: assert tab_stop is tab_stop_ count += 1 assert count == expected_count assert TabStop_.call_args_list == expected_calls def it_can_get_a_tab_stop_by_index(self, index_fixture): tab_stops, idx, TabStop_, tab, tab_stop_ = index_fixture tab_stop = tab_stops[idx] TabStop_.assert_called_once_with(tab) assert tab_stop is tab_stop_ def it_raises_on_indexed_access_when_empty(self): tab_stops = TabStops(element("w:pPr")) with pytest.raises(IndexError): tab_stops[0] def it_can_add_a_tab_stop(self, add_tab_fixture): tab_stops, position, kwargs, expected_xml = add_tab_fixture tab_stops.add_tab_stop(position, **kwargs) assert tab_stops._element.xml == expected_xml def it_can_delete_a_tab_stop(self, del_fixture): tab_stops, idx, expected_xml = del_fixture del tab_stops[idx] assert tab_stops._element.xml == expected_xml def it_raises_on_del_idx_invalid(self, del_raises_fixture): tab_stops, idx = del_raises_fixture with pytest.raises(IndexError) as exc: del tab_stops[idx] assert exc.value.args[0] == "tab index out of range" def it_can_clear_all_its_tab_stops(self, clear_all_fixture): tab_stops, expected_xml = clear_all_fixture tab_stops.clear_all() assert tab_stops._element.xml == expected_xml # fixture -------------------------------------------------------- @pytest.fixture( params=[ "w:pPr", "w:pPr/w:tabs/w:tab{w:pos=42}", "w:pPr/w:tabs/(w:tab{w:pos=24},w:tab{w:pos=42})", ] ) def clear_all_fixture(self, request): pPr_cxml = request.param tab_stops = TabStops(element(pPr_cxml)) expected_xml = xml("w:pPr") return tab_stops, expected_xml @pytest.fixture( params=[ ("w:pPr/w:tabs/w:tab{w:pos=42}", 0, "w:pPr"), ( "w:pPr/w:tabs/(w:tab{w:pos=24},w:tab{w:pos=42})", 0, "w:pPr/w:tabs/w:tab{w:pos=42}", ), ( "w:pPr/w:tabs/(w:tab{w:pos=24},w:tab{w:pos=42})", 1, "w:pPr/w:tabs/w:tab{w:pos=24}", ), ] ) def del_fixture(self, request): pPr_cxml, idx, expected_cxml = request.param tab_stops = TabStops(element(pPr_cxml)) expected_xml = xml(expected_cxml) return tab_stops, idx, expected_xml @pytest.fixture( params=[ ("w:pPr", 0), ("w:pPr/w:tabs/w:tab{w:pos=42}", 1), ] ) def del_raises_fixture(self, request): tab_stops_cxml, idx = request.param tab_stops = TabStops(element(tab_stops_cxml)) return tab_stops, idx @pytest.fixture( params=[ ("w:pPr", Twips(42), {}, "w:pPr/w:tabs/w:tab{w:pos=42,w:val=left}"), ( "w:pPr", Twips(72), {"alignment": WD_TAB_ALIGNMENT.RIGHT}, "w:pPr/w:tabs/w:tab{w:pos=72,w:val=right}", ), ( "w:pPr", Twips(24), {"alignment": WD_TAB_ALIGNMENT.CENTER, "leader": WD_TAB_LEADER.DOTS}, "w:pPr/w:tabs/w:tab{w:pos=24,w:val=center,w:leader=dot}", ), ( "w:pPr/w:tabs/w:tab{w:pos=42}", Twips(72), {}, "w:pPr/w:tabs/(w:tab{w:pos=42},w:tab{w:pos=72,w:val=left})", ), ( "w:pPr/w:tabs/w:tab{w:pos=42}", Twips(24), {}, "w:pPr/w:tabs/(w:tab{w:pos=24,w:val=left},w:tab{w:pos=42})", ), ( "w:pPr/w:tabs/w:tab{w:pos=42}", Twips(42), {}, "w:pPr/w:tabs/(w:tab{w:pos=42},w:tab{w:pos=42,w:val=left})", ), ] ) def add_tab_fixture(self, request): pPr_cxml, position, kwargs, expected_cxml = request.param tab_stops = TabStops(element(pPr_cxml)) expected_xml = xml(expected_cxml) return tab_stops, position, kwargs, expected_xml @pytest.fixture( params=[ ("w:pPr/w:tabs/w:tab{w:pos=0}", 0), ("w:pPr/w:tabs/(w:tab{w:pos=1},w:tab{w:pos=2},w:tab{w:pos=3})", 1), ("w:pPr/w:tabs/(w:tab{w:pos=4},w:tab{w:pos=5},w:tab{w:pos=6})", 2), ] ) def index_fixture(self, request, TabStop_, tab_stop_): pPr_cxml, idx = request.param pPr = element(pPr_cxml) tab = pPr.xpath("./w:tabs/w:tab")[idx] tab_stops = TabStops(pPr) return tab_stops, idx, TabStop_, tab, tab_stop_ @pytest.fixture( params=[ ("w:pPr", 0), ("w:pPr/w:tabs/w:tab{w:pos=2880}", 1), ("w:pPr/w:tabs/(w:tab{w:pos=2880},w:tab{w:pos=5760})", 2), ] ) def iter_fixture(self, request, TabStop_, tab_stop_): pPr_cxml, expected_count = request.param pPr = element(pPr_cxml) tab_elms = pPr.xpath("//w:tab") tab_stops = TabStops(pPr) expected_calls = [call(tab) for tab in tab_elms] return tab_stops, expected_count, tab_stop_, TabStop_, expected_calls @pytest.fixture( params=[ ("w:pPr", 0), ("w:pPr/w:tabs/w:tab{w:pos=2880}", 1), ] ) def len_fixture(self, request): tab_stops_cxml, expected_value = request.param tab_stops = TabStops(element(tab_stops_cxml)) return tab_stops, expected_value # fixture components --------------------------------------------- @pytest.fixture def TabStop_(self, request, tab_stop_): return class_mock(request, "docx.text.tabstops.TabStop", return_value=tab_stop_) @pytest.fixture def tab_stop_(self, request): return instance_mock(request, TabStop)