1# -*- coding: UTF-8 -*-
2from behave import step, then
3from common_steps import wait_until
4from dogtail.predicate import GenericPredicate
5from dogtail.rawinput import keyCombo
6from time import time, sleep
7from gi.repository import GLib
8import pyatspi
9
10
11@step(u'Select "{name}" addressbook')
12def select_addressbook(context, name, password=None):
13    cells = context.app.findChildren(
14        GenericPredicate(name=name, roleName='table cell'))
15    visible_cells = filter(lambda x: x.showing, cells)
16    if visible_cells == []:
17        raise RuntimeError("Cannot find addressbook '%s'" % name)
18    visible_cells[0].click()
19    # Wait for addressbook to load
20    try:
21        spinner = context.app.findChild(
22            GenericPredicate(name='Spinner'), retry=False, requireResult=False)
23        if spinner:
24            start_time = time()
25            while spinner.showing:
26                sleep(1)
27                if (time() - start_time) > 180:
28                    raise RuntimeError("Contacts take too long to synchronize")
29    except (GLib.GError, TypeError):
30        pass
31
32
33@step(u'Change categories view to "{category}"')
34def change_categories_view(context, category):
35    labels = context.app.findChildren(
36        lambda x: x.labeller.name == 'Show:' and x.showing)
37    if labels == []:
38        raise RuntimeError("Cannot find category switcher")
39    labels[0].combovalue = category
40
41
42@step(u'Delete selected contact')
43def delete_selected_contact(context):
44    context.app.menu('Edit').click()
45    mnu = context.app.menu('Edit').menuItem("Delete Contact")
46    if pyatspi.STATE_ENABLED in mnu.getState().getStates():
47        context.app.menu('Edit').menuItem("Delete Contact").click()
48
49        alert = context.app.child(roleName='alert', name='Question')
50        alert.button('Delete').click()
51    context.execute_steps(u"* Wait for email to synchronize")
52
53
54@step(u'Delete all contacts containing "{part}"')
55def delete_all_contacts_containing(context, part):
56    context.app.search_bar.grab_focus()
57    for attempts in range(0, 10):
58        try:
59            context.app.search_bar.text = part
60            break
61        except (GLib.GError, AttributeError):
62            sleep(0.1)
63            continue
64    keyCombo("<Enter>")
65    context.execute_steps(u"* Wait for email to synchronize")
66    context.app.search_bar.grab_focus()
67    keyCombo("<Tab>")
68    sleep(3)
69    heading = context.app.findChild(
70        GenericPredicate(roleName='heading'),
71        retry=False, requireResult=False)
72    if heading:
73        keyCombo("<Control>a")
74        context.execute_steps(u"* Delete selected contact")
75        sleep(3)
76
77
78@step(u'Create a new contact')
79def create_a_new_contact(context):
80    context.app.menu('File').click()
81    context.app.menu('File').menu('New').point()
82    context.app.menu('File').menu('New').menuItem("Contact").click()
83    context.execute_steps(u"Then Contact editor window is opened")
84
85
86def get_element_by_name(contact_editor, name, section=None):
87    """Get a field object by name in section (if specified)"""
88    element = None
89    if section:
90        panel = contact_editor.findChild(
91            GenericPredicate(roleName='panel', name=section), retry=False, requireResult=False)
92        if not panel:
93            # Other section is not a panel, but a toggle button
94            panel = contact_editor.child(roleName='toggle button', name=section)
95        element = panel.childLabelled(name)
96    else:
97        label = contact_editor.findChild(
98            GenericPredicate(label=name), retry=False, requireResult=False)
99        if not label:
100            # In case childLabelled is missing
101            # Find a filler with this name and get its text child
102            element = contact_editor.child(
103                roleName='filler', name=name).child(roleName='text')
104        else:
105            element = contact_editor.childLabelled(name)
106    if element:
107        return element
108    else:
109        raise RuntimeError("Cannot find element named '%s' in section '%s'" % (
110            name, section))
111
112
113@step(u'Set "{field_name}" in contact editor to "{field_value}"')
114def set_field_to_value(context, field_name, field_value):
115    element = get_element_by_name(context.app.contact_editor, field_name)
116    if element.roleName == "text":
117        element.text = field_value
118    elif element.roleName == "combo box":
119        if element.combovalue != field_value:
120            element.combovalue = field_value
121
122
123@step(u'Save the contact')
124def save_contact(context):
125    context.app.contact_editor.button('Save').click()
126    assert wait_until(lambda x: not x.showing, context.app.contact_editor),\
127        "Contact Editor was not hidden"
128    assert wait_until(lambda x: x.dead, context.app.contact_editor),\
129        "Contact Editor was not closed"
130    context.app.contact_editor = None
131
132
133@step(u'Refresh addressbook')
134def refresh_addressbook(context):
135    #Clear the search
136    icons = context.app.search_bar.findChildren(lambda x: x.roleName == 'icon')
137    if icons != []:
138        icons[-1].click()
139    else:
140        for attempts in range(0, 10):
141            try:
142                context.app.search_bar.text = ''
143                break
144            except (GLib.GError, AttributeError):
145                sleep(0.1)
146                continue
147        context.app.search_bar.grab_focus()
148        keyCombo('<Enter>')
149    context.execute_steps(u"* Wait for email to synchronize")
150
151
152@step(u'Select "{contact_name}" contact list')
153@step(u'Select "{contact_name}" contact')
154def select_contact_with_name(context, contact_name):
155    # heading shows the name of currently selected contact
156    # We have to keep on pressing Tab to select the next contact
157    # Until we meet the first contact
158    # WARNING - what if we will have two identical contacts?
159    fail = False
160    selected_contact = None
161
162    # HACK
163    # To make the contact table appear
164    # we need to focus on search window
165    # and send Tabs to have the first contact focused
166    context.app.search_bar.grab_focus()
167    sleep(0.1)
168    # Switch to 'Any field contains' (not reachable in 3.6)
169    icons = context.app.search_bar.findChildren(GenericPredicate(roleName='icon'))
170
171    if icons != []:
172        icons[0].click()
173        wait_until(lambda x: x.findChildren(
174            GenericPredicate(roleName='check menu item', name='Any field contains')) != [],
175            context.app)
176        context.app.menuItem('Any field contains').click()
177        for attempts in range(0, 10):
178            try:
179                context.app.search_bar.text = contact_name
180                break
181            except (GLib.GError, AttributeError):
182                sleep(0.1)
183                continue
184        keyCombo("<Enter>")
185        context.app.search_bar.grab_focus()
186
187    keyCombo("<Tab>")
188    first_contact_name = context.app.child(roleName='heading').text
189
190    while True:
191        selected_contact = context.app.child(roleName='heading')
192        if selected_contact.text == contact_name:
193            fail = False
194            break
195        keyCombo("<Tab>")
196        # Wait until contact data is being rendered
197        sleep(1)
198        if first_contact_name == selected_contact.text:
199            fail = True
200            break
201
202    context.assertion.assertFalse(
203        fail, "Can't find contact named '%s'" % contact_name)
204    context.selected_contact_text = selected_contact.text
205
206
207@step(u'Open contact editor for selected contact')
208def open_contact_editor_for_selected_contact(context):
209    context.app.menu('File').click()
210    context.app.menu('File').menuItem('Open Contact').click()
211    context.execute_steps(u"""
212        Then Contact editor window with title "Contact Editor - %s" is opened
213        """ % context.selected_contact_text)
214
215
216@then(u'"{field}" property is set to "{expected}"')
217def property_in_contact_window_is_set_to(context, field, expected):
218    element = get_element_by_name(context.app.contact_editor, field)
219    actual = None
220    if element.roleName == "text":
221        actual = element.text
222    elif element.roleName == "combo box":
223        actual = element.combovalue
224        if actual == '':
225            actual = element.textentry('').text
226    assert unicode(actual) == expected, "Incorrect value"
227
228
229def get_combobox_textbox_object(contact_editor, section, scroll_to_bottom=True):
230    """Get a list of paired 'combobox-textbox' objects in contact editor"""
231    section_names = {
232        'Ims': 'Instant Messaging',
233        'Phones': 'Telephone',
234        'Emails': 'Email'}
235    section = section_names[section.capitalize()]
236    lbl = contact_editor.child(roleName='toggle button', name=section)
237    panel = lbl.findAncestor(GenericPredicate(roleName='filler'))
238    textboxes = panel.findChildren(GenericPredicate(roleName='text'))
239
240    # Scroll to the bottom of the page if needed
241    pagetab = panel.findAncestor(GenericPredicate(roleName='page tab'))
242    for scroll in pagetab.findChildren(lambda x: x.roleName == 'scroll bar'):
243        if scroll_to_bottom:
244            scroll.value = scroll.maxValue
245        else:
246            scroll.value = 0
247
248    # Expand section if button exists
249    button = panel.findChild(
250        GenericPredicate(roleName='push button', name=section),
251        retry=False, requireResult=False)
252    # Expand button if any of textboxes is not visible
253    if button and (False in [x.showing for x in textboxes]):
254        button.click()
255
256    comboboxes = panel.findChildren(GenericPredicate(roleName='combo box'))
257
258    # Rearrange comboboxes and textboxes according to their position
259    result = []
260    for combo in comboboxes:
261        combo_row = combo.position[1]
262        matching_textboxes = [
263            x for x in textboxes
264            if ((x.position[1] - combo_row) == 0) and (x.position[0] > combo.position[0])]
265        if (matching_textboxes != []):
266            correct_textbox = min(matching_textboxes, key=lambda x: x.position[0])
267            result.append((combo, correct_textbox))
268
269    comboboxes = [x[0] for x in result][::-1]
270    textboxes = [x[1] for x in result][::-1]
271
272    return (textboxes, comboboxes, button)
273
274
275@step(u'Set {section} in contact editor to')
276def set_contact_emails_to_value(context, section):
277    (textboxes, comboboxes, collapse_button) = get_combobox_textbox_object(
278        context.app.contact_editor, section)
279
280    # clear existing data
281    for textbox in textboxes:
282        textbox.text = ""
283
284    for index, row in enumerate(context.table.rows):
285        # Check that we have sufficient amount of textboxes
286        # If not - click plus buttons until we have enough
287        if index == len(textboxes):
288            textboxes[0].parent.child(roleName="push button").click()
289            (textboxes, comboboxes, collapse_button) = get_combobox_textbox_object(
290                context.app.contact_editor, section)
291        textboxes[index].text = row['Value']
292        if comboboxes[index].combovalue != row['Field']:
293            comboboxes[index].combovalue = row['Field']
294
295
296@then(u'{section} are set to')
297def emails_are_set_to(context, section):
298    (textboxes, comboboxes, collapse_button) = get_combobox_textbox_object(
299        context.app.contact_editor, section, section == 'IMs')
300
301    actual = []
302    for index, textbox in enumerate(textboxes):
303        combo_value = textbox.text
304        if combo_value.strip() != '':
305            type_value = comboboxes[index].combovalue
306            actual.append({'Field': unicode(type_value), 'Value': unicode(combo_value)})
307    actual = sorted(actual)
308
309    expected = []
310    for row in context.table:
311        expected.append({'Field': row['Field'], 'Value': row['Value']})
312    expected = sorted(expected)
313
314    assert actual == expected, "Incorrect %s value:\nexpected:%s\n but was:%s" % (
315        row['Field'], expected, actual)
316
317
318@step(u'Tick "Wants to receive HTML mail" checkbox')
319def tick_checkbox(context):
320    context.app.contact_editor.childNamed("Wants to receive HTML mail").click()
321
322
323@step(u'"Wants to receive HTML mail" checkbox is ticked')
324def checkbox_is_ticked(context):
325    check_state = context.app.childNamed("Wants to receive HTML mail").checked
326    assert check_state, "Incorrect checkbox state"
327
328
329@step(u'Switch to "{name}" tab in contact editor')
330def switch_to_tab(context, name):
331    context.app.contact_editor.tab(name).click()
332
333
334@step(u'Set the following properties in contact editor')
335def set_properties(context):
336    for row in context.table.rows:
337        context.execute_steps(u"""
338            * Set "%s" in contact editor to "%s"
339        """ % (row['Field'], row['Value']))
340
341
342@step(u'The following properties in contact editor are set')
343def verify_properties(context):
344    for row in context.table.rows:
345        context.execute_steps(u"""
346            Then "%s" property is set to "%s"
347        """ % (row['Field'], row['Value']))
348
349
350@step(u'Set the following properties in "{section}" section of contact editor')
351def set_properties_in_section(context, section):
352    for row in context.table.rows:
353        context.execute_steps(u"""
354            * Set "%s" in "%s" section of contact editor to "%s"
355        """ % (row['Field'], section, row['Value']))
356
357
358@step(u'The following properties in "{section}" section of contact editor are set')
359def verify_properties_in_section(context, section):
360    for row in context.table.rows:
361        context.execute_steps(u"""
362            Then "%s" property in "%s" section is set to "%s"
363        """ % (row['Field'], section, row['Value']))
364
365
366@step(u'Set the following note for the contact')
367def set_note_for_contact(context):
368    context.app.contact_editor.child(
369        roleName='page tab', name='Notes').textentry('').text = context.text
370
371
372@then(u'The following note is set for the contact')
373def verify_note_set_for_contact(context):
374    actual = context.app.contact_editor.child(
375        roleName='page tab', name='Notes').textentry('').text
376    expected = context.text
377    assert actual == expected,\
378        "Incorrect note value:\nexpected:%s\n but was:%s" % (expected, actual)
379
380
381@step(u'Set "{field_name}" in "{section}" section of contact editor to "{field_value}"')
382def set_field_in_section_to_value(context, field_name, section, field_value):
383    element = get_element_by_name(
384        context.app.contact_editor, field_name, section=section)
385    if element.roleName == "text":
386        element.text = field_value
387    elif element.roleName == "combo box":
388        element.combovalue = field_value
389