# encoding: utf-8 """ Test suite for the docx.text.tabstops module, containing the TabStops and TabStop objects. """ from __future__ import ( absolute_import, division, print_function, unicode_literals ) from docx.enum.text import WD_TAB_ALIGNMENT, WD_TAB_LEADER from docx.shared import Twips from docx.text.tabstops import TabStop, TabStops import pytest from ..unitutil.cxml import element, xml from ..unitutil.mock import call, class_mock, instance_mock class DescribeTabStop(object): 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(object): 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)