From b055bf8885281a4d5e0577713a8f3f0c79b37d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 19 Dec 2024 12:04:37 +0100 Subject: [PATCH] tests: update Thunderbird interactions 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 --- splitgpg2tests/tests.py | 16 ++-- tests/test_evolution.py | 32 +++++--- tests/test_thunderbird.py | 160 ++++++++++++++++++++++++++++---------- 3 files changed, 152 insertions(+), 56 deletions(-) diff --git a/splitgpg2tests/tests.py b/splitgpg2tests/tests.py index 01b9719..62b6735 100644 --- a/splitgpg2tests/tests.py +++ b/splitgpg2tests/tests.py @@ -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 @@ -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"); @@ -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( diff --git a/tests/test_evolution.py b/tests/test_evolution.py index 8f69b0d..d6fd3a8 100644 --- a/tests/test_evolution.py +++ b/tests/test_evolution.py @@ -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 @@ -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 @@ -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 @@ -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') diff --git a/tests/test_thunderbird.py b/tests/test_thunderbird.py index 77081b7..eb8d4ba 100644 --- a/tests/test_thunderbird.py +++ b/tests/test_thunderbird.py @@ -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( @@ -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): @@ -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( @@ -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( @@ -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: @@ -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: @@ -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 @@ -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('s') @@ -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') @@ -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')