Skip to content

Commit

Permalink
tests: update Thunderbird/Evolution interactions
Browse files Browse the repository at this point in the history
Sync with app-linux-split-gpg, especially the following commits:
a652de5 test: avoid false negative from sending status dialog
4bcba03 tests: update for Thunderbird 102
15fda8c tests: disable end-of-year message, and similar popups
b9c13d0 tests: fix clicking top buttons in evolution
d65f098 tests: use distribution's dogtail package
00394da tests: try harder to avoid donation prompt during tests
3bf5616 tests: update for Thunderbird 115
7477c2d tests: switch from smtpd to aiosmtpd
17f96e0 tests: handle both Save and Save All dialogs
e46af5f tests: adjust for Thunderbird 128
  • Loading branch information
marmarek committed Dec 21, 2024
1 parent bd30173 commit b2fb67d
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 56 deletions.
16 changes: 9 additions & 7 deletions splitgpg2tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,18 +360,18 @@ def setUp(self):

# run as root to not deal with /var/mail permission issues
self.frontend.run(
'touch /var/mail/user; chown user:user /var/mail/user', user='root',
'mkdir -p Mail/new Mail/cur Mail/tmp',
wait=True)

# SMTP configuration
self.smtp_server = self.frontend.run(
'python3 /usr/share/split-gpg2-tests/test_smtpd.py',
user='root', passio_popen=True)
'aiosmtpd -n -c aiosmtpd.handlers.Mailbox /home/user/Mail',
passio_popen=True)

# IMAP configuration
self.imap_pw = "pass"
self.frontend.run(
'echo "mail_location=mbox:~/Mail:INBOX=/var/mail/%u" |\
'echo "mail_location=maildir:~/Mail" |\
sudo tee /etc/dovecot/conf.d/100-mail.conf', wait=True)
self.frontend.run('sudo systemctl restart dovecot', wait=True)
self.frontend.run( # set a user password because IMAP needs one for auth
Expand Down Expand Up @@ -422,6 +422,8 @@ def setup_tb_profile(self, setup_openpgp):
user_pref("mail.identity.id1.useremail", "user@localhost");
user_pref("mail.identity.id1.smtpServer", "smtp1");
user_pref("mail.identity.id1.compose_html", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false); // avoid message popups
user_pref("app.donation.eoy.version.viewed", 100); // avoid message popups
"""
imap_server = """
user_pref("mail.server.server1.userName", "user");
Expand Down Expand Up @@ -542,11 +544,11 @@ def setUp(self):

# run as root to not deal with /var/mail permission issues
self.frontend.run(
'touch /var/mail/user; chown user /var/mail/user', user='root',
'mkdir -p Mail/new Mail/cur Mail/tmp',
wait=True)
self.smtp_server = self.frontend.run(
'python3 /usr/share/split-gpg2-tests/test_smtpd.py',
user='root', passio_popen=True)
'aiosmtpd -n -c aiosmtpd.handlers.Mailbox /home/user/Mail',
passio_popen=True)

p = self.frontend.run(
'PYTHONPATH=$HOME/dogtail python3 {} setup 2>&1'.format(
Expand Down
32 changes: 23 additions & 9 deletions tests/test_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ def open_accounts(app):
def get_sibling_offset(node, offset):
return node.parent.children[node.indexInParent+offset]

def get_sibling_button_maybe(button):
try:
# if there is a sibling button (without the name) that's the one that works
button_sibling = button.parent.children[button.indexInParent + 1]
if button_sibling.roleName == "push button":
return button_sibling
except KeyError:
pass
return button

def add_local_account(app):
accounts_tab = None
settings = None
Expand All @@ -78,16 +88,16 @@ def add_local_account(app):
wizard.button('Next').doActionNamed('click')
# Receiving Email tab
time.sleep(2)
wizard.menuItem('Local delivery').doActionNamed('click')
wizard.childLabelled('Local Delivery File:').parent.button('(None)').\
wizard.menuItem('Maildir-format mail directories').doActionNamed('click')
wizard.childLabelled('Mail Directory:', showingOnly=True).parent.menuItem('Other…').\
doActionNamed('click')
file_chooser = app.child('Choose a local delivery file',
file_chooser = app.child('Choose a Maildir mail directory',
roleName='file chooser')
file_chooser.child('File System Root').doActionNamed('click')
file_chooser.child('var').doActionNamed('activate')
file_chooser.child('spool').doActionNamed('activate')
file_chooser.child('mail').doActionNamed('activate')
file_chooser.child('home').doActionNamed('activate')
file_chooser.child('user').doActionNamed('activate')
file_chooser.child('Mail').doActionNamed('activate')
file_chooser.button('Open').doActionNamed('click')
time.sleep(1)
wizard.button('Next').doActionNamed('click')
# Receiving Options tab
Expand Down Expand Up @@ -138,7 +148,9 @@ def attach(app, compose_window, path):
file_chooser.button('Attach').doActionNamed('click')

def send_email(app, sign=False, encrypt=False, inline=False, attachment=None):
app.button('New').doActionNamed('click')
new_button = app.button('New')
new_button = get_sibling_button_maybe(new_button)
new_button.doActionNamed('click')
new_message = app.child('Compose Message', roleName='frame')
new_message.textentry('To:').text = 'user@localhost,'
new_message.childLabelled('Subject:').text = subject
Expand All @@ -160,8 +172,10 @@ def send_email(app, sign=False, encrypt=False, inline=False, attachment=None):
new_message.button('Send').doActionNamed('click')

def receive_message(app, signed=False, encrypted=False, attachment=None):
app.button('Send / Receive').doActionNamed('click')
app.child(name='Inbox.*', roleName='table cell').doActionNamed('edit')
send_receive = app.button('Send / Receive')
send_receive = get_sibling_button_maybe(send_receive)
send_receive.doActionNamed('click')
app.child(name='Inbox .*', roleName='table cell').doActionNamed('edit')
messages = app.child('Messages', roleName='panel')
messages.child(subject).grabFocus()
message = app.child('Evolution Mail Display', roleName='document web')
Expand Down
160 changes: 120 additions & 40 deletions tests/test_thunderbird.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def wrapper(*args, **kwargs):
func(*args, **kwargs)
break # if successful
except Exception as e:
if retry == max_tries:
if retry == max_tries-1:
raise e
else:
print("failed during setup in {}.\n Retrying".format(
Expand Down Expand Up @@ -151,20 +151,27 @@ def export_pub_key():

@retry_if_failed(max_tries=3)
def enter_imap_passwd(tb):
# check new mail so client can realize IMAP requires entering a password
get_messages(tb)
try:
pass_prompt = tb.app.findChild(orPredicate(
GenericPredicate(name='Enter your password for user', roleName='frame'),
GenericPredicate(name='Enter your password for user', roleName='dialog')
))
except tree.SearchError:
# check new mail so client can realize IMAP requires entering a password
get_messages(tb)
# password entry
pass_prompt = tb.app.findChild(orPredicate(
GenericPredicate(name='Enter your password for user', roleName='frame'),
GenericPredicate(name='Enter your password for user', roleName='dialog')
))
pass_textbox = pass_prompt.findChild(GenericPredicate(roleName='password text'))
pass_textbox.text = tb.imap_pw
pass_textbox.typeText(tb.imap_pw)
pass_prompt.childNamed("Use Password Manager to remember this password.")\
.doActionNamed('check')
pass_prompt.findChild(orPredicate(
GenericPredicate(name='OK', roleName='push button'), # tb < 91
GenericPredicate(name='Sign in', roleName='push button')) # tb >= 91
GenericPredicate(name='Sign in', roleName='push button'), # tb >= 91, tb < 128
GenericPredicate(name='OK', roleName='button')) # tb >= 128
).doActionNamed('press')

def accept_qubes_attachments(tb):
Expand Down Expand Up @@ -237,8 +244,14 @@ def configure_openpgp_account(tb):
accept_dialog = tb.app.findChild(orPredicate(
GenericPredicate(name='.*(%s).*' % keyid),
GenericPredicate(name='.[0-9A-F]*%s' % keyid),
)).parent
accept_dialog.childNamed('OK').doActionNamed('press')
GenericPredicate(name='ID: 0x%s' % keyid),
)).parent.parent
try:
accept_dialog.childNamed("Accepted.*").doActionNamed("select")
except tree.SearchError:
# old TB
pass
accept_dialog.childNamed('OK|Import').doActionNamed('press')
tb.app.childNamed('Success! Keys imported.*').childNamed('OK').doActionNamed(
'press')
doubleClick(*key_manager.findChild(
Expand All @@ -256,16 +269,37 @@ def configure_openpgp_account(tb):


def get_messages(tb):
tb.app.child(name='user@localhost',
roleName='table row').doActionNamed('activate')
tb.app.button('Get Messages').doActionNamed('press')
tb.app.menuItem('Get All New Messages').doActionNamed('click')
tb.app.child(name='Inbox.*', roleName='table row').doActionNamed(
'activate')
try:
# TB >= 115
try:
# TB >= 128
tb.app.child('Get Messages', roleName='button').doActionNamed('press')
except tree.SearchError:
# TB < 128
tb.app.button('Get Messages').doActionNamed('press')
tb.app.child(name='Inbox.*', roleName='tree item').doActionNamed(
'activate')
except tree.SearchError:
# TB < 115
tb.app.child(name='user@localhost',
roleName='table row').doActionNamed('activate')
tb.app.button('Get Messages').doActionNamed('press')
tb.app.menuItem('Get All New Messages').doActionNamed('click')
tb.app.child(name='Inbox.*', roleName='table row').doActionNamed(
'activate')


def attach(tb, compose_window, path):
compose_window.button('Attach').button('Attach').doActionNamed('press')
compose_window.button('Attach').menuItem('File.*').doActionNamed('click')
try:
# TB >= 128
compose_window.child('Attach', roleName='button').\
doActionNamed('press')
compose_window.child('Attach', roleName='button').\
menuItem('File.*').doActionNamed('click')
except tree.SearchError:
# TB < 128
compose_window.button('Attach').button('Attach').doActionNamed('press')
compose_window.button('Attach').menuItem('File.*').doActionNamed('click')
# for some reason on some thunderbird versions do not expose 'Attach File'
# dialog through accessibility API, use xdotool instead
subprocess.check_call(
Expand Down Expand Up @@ -293,18 +327,25 @@ def attach(tb, compose_window, path):

def send_email(tb, sign=False, encrypt=False, inline=False, attachment=None):
config.searchCutoffCount = 20
write = tb.app.button('Write')
try:
# TB >= 128
write = tb.app.child(name='New Message', roleName='button')
except tree.SearchError:
try:
write = tb.app.button('New Message')
except tree.SearchError:
write = tb.app.button('Write')
config.searchCutoffCount = defaultCutoffCount
write.doActionNamed('press')
compose = tb.app.child(name='Write: .*', roleName='frame')
to_entry = compose.findChild(TBEntry(name='To'))
to_entry.text = 'user@localhost'
to_entry.typeText('user@localhost')
# lets thunderbird settle down on default values (after filling recipients)
time.sleep(1)
subject_entry = compose.findChild(
orPredicate(GenericPredicate(name='Subject:', roleName='entry'),
TBEntry(name='Subject')))
subject_entry.text = subject
subject_entry.typeText(subject)
try:
compose_document = compose.child(roleName='document web')
try:
Expand All @@ -315,18 +356,29 @@ def send_email(tb, sign=False, encrypt=False, inline=False, attachment=None):
except tree.SearchError:
compose.child(
roleName='document frame').text = 'This is test message'
security = compose.findChild(
GenericPredicate(name='Security', roleName='push button'))
try:
# TB >= 128
security = compose.findChild(
GenericPredicate(name='Security|OpenPGP', roleName='button'))
except tree.SearchError:
# TB < 128
security = compose.findChild(
GenericPredicate(name='Security|OpenPGP', roleName='push button'))
security.doActionNamed('press')
sign_button = security.childNamed('Digitally Sign This Message')
encrypt_button = security.childNamed('Require Encryption')
sign_button = security.childNamed('Digitally Sign.*')
encrypt_button = security.childNamed('Require Encryption|Encrypt')
if sign_button.checked != sign:
sign_button.doActionNamed('click')
if encrypt_button.checked != encrypt:
encrypt_button.doActionNamed('click')
if attachment:
attach(tb, compose, attachment)
compose.button('Send').doActionNamed('press')
try:
# TB >= 128
compose.child('Send', roleName='button').doActionNamed('press')
except tree.SearchError:
# TB < 128
compose.button('Send').doActionNamed('press')
config.searchCutoffCount = 5
try:
if encrypt:
Expand Down Expand Up @@ -363,16 +415,36 @@ def send_email(tb, sign=False, encrypt=False, inline=False, attachment=None):

def receive_message(tb, signed=False, encrypted=False, attachment=None):
get_messages(tb)
config.searchCutoffCount = 5
if encrypted:
config.searchCutoffCount = 5
try:
# TB >= 128
tb.app.child(name='user[^,]*, .*, \.\.\..*',
roleName='table row').doActionNamed('clickAncestor')
except tree.SearchError:
try:
# TB >= 115
tb.app.child(name='user[^,]*, .*, \.\.\..*',
roleName='tree item').doActionNamed('activate')
except tree.SearchError:
# TB < 115
tb.app.child(name='Encrypted Message .*|.*\.\.\. .*',
roleName='table row').doActionNamed('activate')
finally:
config.searchCutoffCount = defaultCutoffCount
try:
tb.app.child(name='Encrypted Message .*|.*\.\.\. .*',
roleName='table row').doActionNamed('activate')
# TB >= 128
tb.app.child(name='.*{}.*'.format(subject),
roleName='table row').doActionNamed('clickAncestor')
except tree.SearchError:
pass
finally:
config.searchCutoffCount = defaultCutoffCount
tb.app.child(name='.*{}.*'.format(subject),
roleName='table row').doActionNamed('activate')
try:
# TB >= 115
tb.app.child(name='.*{}.*'.format(subject),
roleName='tree item').doActionNamed('activate')
except tree.SearchError:
# TB < 115
tb.app.child(name='.*{}.*'.format(subject),
roleName='table row').doActionNamed('activate')
# wait a little to TB decrypt/check the message
time.sleep(2)
# dogtail always add '$' at the end of regexp; and also "Escape all
Expand All @@ -399,11 +471,10 @@ def receive_message(tb, signed=False, encrypted=False, attachment=None):
# msg_body = msg.text
config.searchCutoffCount = 5
try:
if signed or encrypted:
tb.app.button('OpenPGP.*').doActionNamed('press')
# 'Message Security - OpenPGP' is an internal label,
# nested 2 levels into the popup
message_security = tb.app.child('Message Security - OpenPGP')
tb.app.button('OpenPGP.*').doActionNamed('press')
# 'Message Security - OpenPGP' is an internal label,
# nested 2 levels into the popup
message_security = tb.app.child('Message Security - OpenPGP')
except tree.SearchError:
# alternative way of opening 'message security'
keyCombo('<Control><Alt>s')
Expand All @@ -430,8 +501,14 @@ def receive_message(tb, signed=False, encrypted=False, attachment=None):
attachment_size = attachment_label.parent.children[
attachment_label.indexInParent + 1 + offset]
assert attachment_size.text[0] != '0'
attachment_save = attachment_label.parent.children[
attachment_label.indexInParent + 2 + offset].button('Save.*')
attachment_save_parent = attachment_label.parent.children[
attachment_label.indexInParent + 2 + offset]
try:
# TB >= 128
attachment_save = attachment_save_parent.child('Save.*', roleName='button')
except tree.SearchError:
# TB < 128
attachment_save = attachment_save_parent.button('Save.*')
try:
# try child button first
attachment_save.children[1].doActionNamed('press')
Expand All @@ -444,11 +521,14 @@ def receive_message(tb, signed=False, encrypted=False, attachment=None):
# for some reasons some Thunderbird versions do not expose 'Attach File'
# dialog through accessibility API, use xdotool instead
save_as = tb.app.findChild(
GenericPredicate(name='Save All Attachments',
GenericPredicate(name='Save All Attachments|Save Attachment',
roleName='file chooser'))
click(*save_as.childNamed('Home').position)
click(*save_as.childNamed('Desktop').position)
save_as.childNamed('Open').doActionNamed('click')
if save_as.name == 'Save Attachment':
save_as.childNamed('Save').doActionNamed('click')
else:
save_as.childNamed('Open').doActionNamed('click')
# save_as = tb.app.dialog('Save .*Attachment.*')
# places = save_as.child(roleName='table',
# name='Places')
Expand Down

0 comments on commit b2fb67d

Please sign in to comment.